\(\newcommand{\Java}{\href{http://java.com/en/}{Java}}\) \(\newcommand{\Python}{\href{https://www.python.org/}{Python}}\) \(\newcommand{\CPP}{\href{http://www.cplusplus.com/}{C++}}\) \(\newcommand{\ST}[1]{{\Blue{\textsf{#1}}}}\) \(\newcommand{\PseudoCode}[1]{{\color{blue}\textsf{#1}}}\) \(%\newcommand{\subheading}[1]{\textbf{\large\color{aaltodgreen}#1}}\) \(\newcommand{\subheading}[1]{\large{\usebeamercolor[fg]{frametitle} #1}}\) \(\newcommand{\Blue}[1]{{\color{flagblue}#1}}\) \(\newcommand{\Red}[1]{{\color{aaltored}#1}}\) \(\newcommand{\Emph}[1]{\emph{\color{flagblue}#1}}\) \(\newcommand{\Engl}[1]{({\em engl.}\ #1)}\) \(\newcommand{\Pointer}{\raisebox{-1ex}{\huge\ding{43}}\ }\) \(\newcommand{\Set}[1]{\{#1\}}\) \(\newcommand{\Setdef}[2]{\{{#1}\mid{#2}\}}\) \(\newcommand{\PSet}[1]{\mathcal{P}(#1)}\) \(\newcommand{\Card}[1]{{\vert{#1}\vert}}\) \(\newcommand{\Tuple}[1]{(#1)}\) \(\newcommand{\Implies}{\Rightarrow}\) \(\newcommand{\Reals}{\mathbb{R}}\) \(\newcommand{\Seq}[1]{(#1)}\) \(\newcommand{\Arr}[1]{[#1]}\) \(\newcommand{\Floor}[1]{{\lfloor{#1}\rfloor}}\) \(\newcommand{\Ceil}[1]{{\lceil{#1}\rceil}}\) \(\newcommand{\Path}[1]{(#1)}\) \(%\newcommand{\Lg}{\lg}\) \(\newcommand{\Lg}{\log_2}\) \(\newcommand{\BigOh}{O}\) \(\newcommand{\Oh}[1]{\BigOh(#1)}\) \(\newcommand{\todo}[1]{\Red{\textbf{TO DO: #1}}}\) \(\newcommand{\NULL}{\textsf{null}}\) \(\newcommand{\Insert}{\ensuremath{\textsc{insert}}}\) \(\newcommand{\Search}{\ensuremath{\textsc{search}}}\) \(\newcommand{\Delete}{\ensuremath{\textsc{delete}}}\) \(\newcommand{\Remove}{\ensuremath{\textsc{remove}}}\) \(\newcommand{\Parent}[1]{\mathop{parent}(#1)}\) \(\newcommand{\ALengthOf}[1]{{#1}.\textit{length}}\) \(\newcommand{\TRootOf}[1]{{#1}.\textit{root}}\) \(\newcommand{\TLChildOf}[1]{{#1}.\textit{leftChild}}\) \(\newcommand{\TRChildOf}[1]{{#1}.\textit{rightChild}}\) \(\newcommand{\TNode}{x}\) \(\newcommand{\TNodeI}{y}\) \(\newcommand{\TKeyOf}[1]{{#1}.\textit{key}}\) \(\newcommand{\PEnqueue}[2]{{#1}.\textsf{enqueue}(#2)}\) \(\newcommand{\PDequeue}[1]{{#1}.\textsf{dequeue}()}\) \(\newcommand{\Def}{\mathrel{:=}}\) \(\newcommand{\Eq}{\mathrel{=}}\) \(\newcommand{\Asgn}{\mathrel{\leftarrow}}\) \(%\newcommand{\Asgn}{\mathrel{:=}}\) \(%\) \(% Heaps\) \(%\) \(\newcommand{\Downheap}{\textsc{downheap}}\) \(\newcommand{\Upheap}{\textsc{upheap}}\) \(\newcommand{\Makeheap}{\textsc{makeheap}}\) \(%\) \(% Dynamic sets\) \(%\) \(\newcommand{\SInsert}[1]{\textsc{insert}(#1)}\) \(\newcommand{\SSearch}[1]{\textsc{search}(#1)}\) \(\newcommand{\SDelete}[1]{\textsc{delete}(#1)}\) \(\newcommand{\SMin}{\textsc{min}()}\) \(\newcommand{\SMax}{\textsc{max}()}\) \(\newcommand{\SPredecessor}[1]{\textsc{predecessor}(#1)}\) \(\newcommand{\SSuccessor}[1]{\textsc{successor}(#1)}\) \(%\) \(% Union-find\) \(%\) \(\newcommand{\UFMS}[1]{\textsc{make-set}(#1)}\) \(\newcommand{\UFFS}[1]{\textsc{find-set}(#1)}\) \(\newcommand{\UFCompress}[1]{\textsc{find-and-compress}(#1)}\) \(\newcommand{\UFUnion}[2]{\textsc{union}(#1,#2)}\) \(%\) \(% Graphs\) \(%\) \(\newcommand{\Verts}{V}\) \(\newcommand{\Vtx}{v}\) \(\newcommand{\VtxA}{v_1}\) \(\newcommand{\VtxB}{v_2}\) \(\newcommand{\VertsA}{V_\textup{A}}\) \(\newcommand{\VertsB}{V_\textup{B}}\) \(\newcommand{\Edges}{E}\) \(\newcommand{\Edge}{e}\) \(\newcommand{\NofV}{\Card{V}}\) \(\newcommand{\NofE}{\Card{E}}\) \(\newcommand{\Graph}{G}\) \(\newcommand{\SCC}{C}\) \(\newcommand{\GraphSCC}{G^\text{SCC}}\) \(\newcommand{\VertsSCC}{V^\text{SCC}}\) \(\newcommand{\EdgesSCC}{E^\text{SCC}}\) \(\newcommand{\GraphT}{G^\text{T}}\) \(%\newcommand{\VertsT}{V^\textup{T}}\) \(\newcommand{\EdgesT}{E^\text{T}}\) \(%\) \(% NP-completeness etc\) \(%\) \(\newcommand{\Poly}{\textbf{P}}\) \(\newcommand{\NP}{\textbf{NP}}\) \(\newcommand{\PSPACE}{\textbf{PSPACE}}\) \(\newcommand{\EXPTIME}{\textbf{EXPTIME}}\)
\(\newcommand{\Lo}{\mathit{lo}}\) \(\newcommand{\Hi}{\mathit{hi}}\) \(\newcommand{\Mid}{\mathit{mid}}\)

Describing algorithms

In an abstract view, an algorithm can be seen as a method that transforms some input to output.

_images/algobox.jpg

For instance, a sorting algorithm transforms an array of elements into another array that contains the same elements but in a sorted order. In order to communicate algorithms to other people, we have to describe them in some way. Of course, one could describe the algorithm by giving its implementation in some concrete programming language. However, in many cases, it can be easier and beneficial to describe an algorithm

  • ​ in a natural language, or

  • ​ in some form of pseudocode.

As an example, recall the binary search algorithm discussed in the CS-A1120 Programming 2 course. In the input/output-level abstraction, the algorithm fulfills the following specification:

  • ​ Input: a sorted array \(A\) of elements and an element \(e\).

  • ​ Output: “true” if \(e\) appears in \(A\), “false” otherwise.

In a natural language (English in this case), we may describe the binary search algorithm in the following structured way:

  1. Initially, consider the whole array.

  2. If the considered array is empty, return “false”

  3. If the middle element of the array is \(e\), return “true”.

  4. Otherwise, if the middle element is greater than \(e\), go to step 2 but only consider the subarray from the beginning to (but not including) the middle element.

  5. Otherwise, the middle element is less than \(e\) and we go to step 2 but consider the subarray from (but not including) the middle element to the end of the array.

In pseudocode, we may describe the binary search algorithm as follows. The actual search is performed in the recursive helper function search, and the currently considered sub-array of \(A\) is indicated by its start and end indices lo and hi.

// Search for the element \(e\) in the sorted array \(A\) binary-search(\(A\), \(e\)): return search(\(A\), \(e\), \(1\), \(\ALengthOf{A}\)) // A recursive helper function doing the actual search search(\(A\), \(e\), \(\Lo\), \(\Hi\)): if \(\Lo \le \Hi\): // sub-array of size 1 or more? \(\Mid \Asgn \Lo + \Floor{(\Hi - \Lo) / 2}\) if \(A[\Mid] \Eq e\): return true else if \(e < A[\Mid]\): return search(\(A\), \(e\), \(\Lo\), \(\Mid-1\)) else: return search(\(A\), \(e\), \(\Mid+1\), \(\Hi\)) else: return false

The pseudocode presentation only expresses the essential operations needed to communicate the algorithm idea — many details, such as type information, are simply omitted. Some notes:

  • ​ As in many programming languages (but not in Scala), array indexing is denoted with the square brackets.

  • ​ In many cases, array indexing starts (i) from \(1\) in pseudocode and in mathematics, but (ii) from \(0\) in real programming languages. Take this into account when implementing algorithms from books, wikipedia, scientific articles and so on to avoid off-by-one errors.

Of course, we may also describe an algorithm by giving an implementation in some programming language. For instance, below is an implementation of the binary search algorithm in Scala.

def binarySearch[T](arr: Array[T], e: T)(using ord: Ordering[T]) =
  def inner(lo: Int, hi: Int): Boolean =
    if lo <= hi then
      val mid = lo + ((hi - lo) / 2)
      val cmp = ord.compare(e, arr(mid))
      if cmp == 0 then true                 // e == arr(mid)
      else if cmp < 0 then inner(lo, mid-1) // e < arr(mid)
      else inner(mid+1, hi)                 // e > arr(mid)
    else
      false
  inner(0, arr.length-1)

Similarly, the same implementation but in C++11:

#include <functional>
#include <vector>

template<typename T>
bool binarySearch(std::vector<T>& arr, T& e) {
  std::function<int(int,int)> inner = [&](int lo, int hi) -> int {
    if(lo <= hi) {
      const int mid = lo + ((hi - lo) / 2);
      if(e == arr[mid])
	return true;
      else if(e < arr[mid])
	return inner(lo, mid-1);
      else // e > arr[mid]
	return inner(mid+1, hi);
    } else
      return false;
  };
  return inner(0, arr.size()-1);
}

These descriptions have the drawback that a reader who is unfamiliar with the applied programming language may get lost in language specific details when trying to understand the core idea of the algorithm. For instance, in the above Scala code, is using ord: Ordering[T] relevant for the algorithm itself? Similarly, a reader who is not familiar with C++ may feel lost when encountering the line std::function<int(int,int)> inner = [&](int low, int hi) -> int {.