\(\) \(%\newcommand{\CLRS}{\href{https://mitpress.mit.edu/books/introduction-algorithms}{Cormen et al}}\) \(\newcommand{\CLRS}{\href{https://mitpress.mit.edu/books/introduction-algorithms}{Introduction to Algorithms, 3rd ed.} (\href{http://libproxy.aalto.fi/login?url=http://site.ebrary.com/lib/aalto/Doc?id=10397652}{Aalto access})}\) \(%\newcommand{\CLRS}{\href{https://mitpress.mit.edu/books/introduction-algorithms}{Introduction to Algorithms, 3rd ed.} (\href{http://libproxy.aalto.fi/login?url=http://site.ebrary.com/lib/aalto/Doc?id=10397652}{online via Aalto lib})}\) \(\newcommand{\SW}{\href{http://algs4.cs.princeton.edu/home/}{Algorithms, 4th ed.}}\) \(%\newcommand{\SW}{\href{http://algs4.cs.princeton.edu/home/}{Sedgewick and Wayne}}\) \(\) \(\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{\BigOh}{\mathcal{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. Note that the same algorithmic idea is given as a slightly different variant: the helper function searches for the index in which the element must occur if it occurs in \(A\) at all, and terminates the search when the considered sub-array is of size 1.

// 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. As in many programming languages (but not in Scala), array indexing is denoted with the square brackets. Please also notice that 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 etc.

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)(implicit ord: Ordering[T]): Boolean = {
  def inner(lo: Int, hi: Int): Boolean = {
    if(lo <= hi) {
      val mid = lo + ((hi - lo) / 2)
      val cmp = ord.compare(e, arr(mid))
      if(cmp == 0) true                 // e == arr(mid)
      else if(cmp < 0) 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 implicit 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 {.