From 67393f42f41ab92219deb549f711121c4dab845b Mon Sep 17 00:00:00 2001 From: Nguyễn Gia Phong Date: Sun, 15 Dec 2019 10:34:58 +0700 Subject: [usth/ICT2.2] Object Oriented Programming --- usth/ICT2.2/midterm/report/report.tex | 928 ++++++++++++++++++++++++++++++++++ 1 file changed, 928 insertions(+) create mode 100644 usth/ICT2.2/midterm/report/report.tex (limited to 'usth/ICT2.2/midterm/report/report.tex') diff --git a/usth/ICT2.2/midterm/report/report.tex b/usth/ICT2.2/midterm/report/report.tex new file mode 100644 index 0000000..627530a --- /dev/null +++ b/usth/ICT2.2/midterm/report/report.tex @@ -0,0 +1,928 @@ +\documentclass[a4paper,12pt]{article} +\usepackage[english,vietnamese]{babel} +\usepackage{amsmath} +\usepackage{amssymb} +\usepackage{booktabs} +\usepackage{lmodern} +\usepackage{graphicx} +\usepackage{hyperref} +\usepackage{lmodern} +\usepackage{forest} +\usepackage{textcomp} +\usepackage[nottoc,numbib]{tocbibind} + +\renewcommand{\thefootnote}{\fnsymbol{footnote}} + +\begin{document} +\setcounter{page}{0} +\thispagestyle{empty} +\vspace*{\stretch{1}} +\begin{flushright} + \setlength{\baselineskip}{1.4\baselineskip} +\textbf{\Huge Midterm Report\\Sorting and Searching} + \noindent\rule{\textwidth}{5pt} + \emph{\Large Object Oriented Programming} + \vspace{\stretch{1}} + + \textbf{by Nguyễn Công Thành, Nguyễn Gia Phong\\ + Nguyễn Văn Tùng, Trần Minh Vương and Trần Hồng Minh\\} + \selectlanguage{english} + \today +\end{flushright} +\vspace*{\stretch{2}} +\pagebreak + +\selectlanguage{english} +\tableofcontents +\pagebreak + +\section{Introduction} +\subsection{Brief Description} +Since the dawn of computer science, sorting and searching algorithms +have drawn a significant amount of attention from researchers, +due to their broad applications in solving a huge number of problems +and sub-problems in many fields. In spite of their straightforward, familiar +statements, these algorithm link with a variety of important programming +concepts, namely data structures, divide and conquer technique and of course +complexity analysis and time–space tradeoffs. + +In this report, we only discuss some of the most basic searching +and sorting algorithms. While trying to implement them, we also analyze +their strengths and weaknesses, as well as the ease and difficulties +we encounter using the language Java and object-oriented programming in general. + +Whilst overall, we follow~\cite{wu}, we also try to make use of newer Java +features to write more generic and concise code. + +This report is licensed under a +\href{http://creativecommons.org/licenses/by-sa/4.0/}{Creative Commons +Attribution-ShareAlike 4.0 International License.} + +\selectlanguage{vietnamese} +\subsection{Authors and Credits} + +The work has been undertaken by group number 11, whose members are listed +in the following table, with Nguyễn Công Thành being our leader. +\begin{center} + \begin{tabular}{c c} + \toprule + Full name & Student ID\\ + \midrule + Nguyễn Công Thành & BI9-210\\ + Nguyễn Gia Phong & BI9-184\\ + Nguyễn Văn Tùng & BI9-229\\ + Trần Minh Vương & BI9-239\\ + Trần Hồng Minh & BI8-114\\ + \bottomrule + \end{tabular} +\end{center} + +We would like to express our special thanks to Dr. Nghiêm Thị Phương, +whose lectures gave us basic understanding on the key principles of +object-oriented programming. Without her guidance, we might never +have a chance to take an in-depth exploration on this topic. +\pagebreak + +\selectlanguage{english} +\section{Searching} +For the sake of simplicity, we only consider searching operating on array-like +data structures with constant-time random access and without duplicated entries. +This affects a few implementation design decision we will be making later on. +The searching problem is then stated in ~\cite[p.~634]{wu} accordingly: +\begin{quote} + Given a value $x$, return the [zero-based] index of $x$ in the array, + if such $x$ exists. Otherwise, return \verb|NOT_FOUND| (-1). +\end{quote} + +In this section, we will discover two searching algorithms: linear search +and binary search. Both of these, albeit being simple, are being used in +many different programming tasks. + +\subsection{Linear Search} +Linear search, by definition, sequentially checks each element of a list +until a match is found or the whole list has been searched. This matches +the algorithm implemented in \verb|java.util.List.indexOf|~\cite[indexOf]{list}. +Given +\begin{verbatim} +var list = java.util.Arrays.asList(4, 20, 6, 9); +\end{verbatim} +where \verb|var| is a Java SE 10 feature for eliding ``the often-unnecessary +manifest declaration of local variable types''~\cite{var}, which, in this case, +is \verb|List|. + +\verb|list.indexOf(6)| would then give 2 and \verb|list.indexOf(7)| returns -1. + +For the matter of completeness, we also try to implement the description +provided by the documentation~\cite[indexOf]{list} +\begin{verbatim} +import java.util.List; + +public class Search +{ + public static final int NOT_FOUND = -1; + + public static linear(List l, Object o) + { + for (int i = 0; i < l.size(); ++i) + if (o == null ? l.get(i) == null : o.equals(l.get(i))) + return i; + return NOT_FOUND; + } +} +\end{verbatim} + +From this dummy implementation, it is trivial that linear search has, +as it name suggests, \emph{linear time} complexity. The nullity check +every iteration adds a bit of overhead, but if performance is ever concerned, +the better tested and optimized standard library should be used instead. +Moving \verb|o == null| to the upper level would require duplicating +the \verb|for| loop, creating redundancy and making the code less readable +and less maintainable. + +Unsurprisingly, 2 and -1 are returned from +\verb|Search.linear(list, 6)| and \verb|Search.linear(list, 7)|, +respectively. + +\subsection{Binary Search} +Binary search find the position of a target value within a sorted array by +``testing the middle of an interval, eliminating the half of the [array] in +which the key cannot lie, and then repeating the procedure iteratively'' +\cite{bsearch}. This could be intuitively implemented using recursion: +\begin{verbatim} +// ... +public class Search +{ + // ... + private static + int binary(List> list, T key, + int low, int high) + { + if (high < low) + return NOT_FOUND; + var mid = (low + high) / 2; + var cmp = list.get(mid).compareTo(key); + if (cmp < 0) + return binary(list, key, mid + 1, high); + if (cmp > 0) + return binary(list, key, low, mid - 1); + return mid; + } +} +\end{verbatim} + +\verb|Search.binary| is declared as a \emph{generic method} to allow +more implicit calls, where the compiler will \emph{infer} the type of \verb|key| +as well as that of each element of \verb|list|~\cite{infer}. This makes +the method work on any type of argument that is a subclass of \verb|Comparable|, +which is required for \verb|Comparable.compareTo|. In fact, +\verb|java.util.Collections.binarySearch|'s declaration follows +the same strategy~\cite[binarySearch]{collections}. + +Instead of directly exposing this method where users need to provide +the lower and higher bound of the indices, we overload it with a wrapper +to ensure encapsulation: +\begin{verbatim} +// ... +public class Search +{ + // ... + public static + int binary(List> list, T key) + { + return binary(list, key, 0, list.size()); + } +} +\end{verbatim} + +To test our implementation, we first need \verb|list| to be sorted. +By providing \verb|null| to its \verb|sort| method, we can sort it +in the elements' natural (ascending) order~\cite[sort]{list}. +As \verb|list| is now [4, 6, 9, 20], \verb|Search.binary(list, 6)| +and \verb|Search.binary(list, 7)| return 1 and -1 respectively. + +\verb|java.util.Collections.binarySearch(list, 7)| gives -3, however, +due to its slightly different behavior: +\begin{quote} + [If \verb|key| is not in the list, returns] \verb|(-(insertion point) - 1)|. + The insertion point is defined as the point at which the key would be + inserted into the list: the index of the first element greater than the key, + or \verb|list.size()| if all elements in the list are less than + the specified key.~\cite[binarySearch]{collections} +\end{quote} + +Since the algorithm only give up the search until the array is bisected to +a nonpositive length (\verb|high < low|), the time-complexity of binary search +is asymptotic to $\log_2 n$, where $n$ is the size of the array. +In other words, on modern 64-bit computers with support for at most 18EB +of address space, the running time is still negligible (64 comparisons). +Despite the use of \verb|List| interface in the implementation, +\verb|Search.binary| is not $\Theta(\lg n)$\footnote{The notation +$f = \Theta(g)$ indicates that $f$ grows at the same rate as $g$.} +on \verb|LinkedList|, which has $\Theta(n)$ random access. + +Since the algorithm only works on ordered lists, it is not +suitable for iteratively inserting to a sorted list, as for linear +data structures, either random access or middle insertion has to be +$\Omega(n)$\footnote{The notation $f = \Omega(g)$ indicates that $f$ grows at +least as as fast as $g$.}. In that case, self-balancing binary search tree +should be used instead. +\pagebreak + +\section{Sorting} +The sorting problem is stated in~\cite[p. 638]{wu} as +\begin{quote} + Given an array of $n$ values, arrange the values into ascending order. +\end{quote} + +This section discusses only three sorting algorithms, +namely selection sort, bubble sort and heapsort. + +\subsection{Selection Sort} +Selection sort could be performed by iterating through every position and select +the minimum element from the current one to the end of the array. The in-place +algorithm could be implemented in Java as follows: +\begin{verbatim} +import java.util.List; + +import static java.util.Collections.swap; + +public class Sort +{ + public static > + void selection(List list) + { + int i, j, m, n = list.size(); + for (i = 0; i < n; ++i) + { + for (m = j = i; j < n; ++j) + if (list.get(j).compareTo(list.get(m)) < 0) + m = j; + swap(list, i, m); + } + } +} +\end{verbatim} + +Since only atomic \verb|int|'s are introduced, the space complexity is +$\Theta(1)$. The time complexity is, in term of swaps, $\Theta(n)$ +and, in term of comparisons, +\[T(n) = \sum_{i=0}^{n-1}\sum_{j=i}^{n-1}1 + = \sum_{i=0}^{n-1}(n - i) + = \sum_{i=1}^{n}i + = \frac{n^2 + n}{2} + = \Theta(n^2)\] + +One could micro-optimize by tweaking \verb|i|'s and \verb|j|'s +bounds by an unit but that does not make the algorithm +any faster asymptotically. + +Just to make sure everything works correctly, we do a simple check +\begin{verbatim} +var list = java.util.Arrays.asList("foo", "bar", "baz"); +Sort.selection(list); +System.out.println(list); +\end{verbatim} +and get \verb|[bar, baz, foo]| printed to \verb|stdout| as expected. + +\subsection{Bubble Sort} +In~\cite[p. 646]{wu}, the author praised bubble sort to finish sooner than +selection sort on average and best cases. Whilst the latter is true, +we will try to explain why the former is generally incorrect. + +To do this, we first need to understand what bubble sort is. +As defined in~\cite[p. 40]{clrs}, the code below operating on +an array \verb|a| of size \verb|n| qualifies as bubble sort. +\begin{verbatim} +for (int i = n - 1; i > 0; --i) + for (int j = 1; j < i; ++j) + if (a[j] < a[j - 1]) + swap(a, j, j - 1); +\end{verbatim} + +This naïve version, like selection sort, has the time complexity $\Theta(n^2)$ +in term of comparisons (it underperform selection sort in term of swaps +however--this will be discussed right after the implementation is completed). +Though, as pointed out in~\cite{bubblebad}, +\begin{quote} + Nearly every description of bubble sort describes + how to terminate the sort early if the vector becomes sorted. +\end{quote} + +Hence, we \emph{upgrade} the pseudocode to +\begin{verbatim} +boolean swapped; +do + { + swapped = false; + for (int j = 1; j < n; ++j) + if (a[j] < a[j - 1]) + { + swap(a, j, j - 1); + swapped = true; + } + } +while (swapped); +\end{verbatim} + +We can do further optimization avoiding comparing the last $t - 1$ items +when running for the $t$-th time of the outer loop, as after iteration $t - 1$, +these have already \emph{bubbled up} to its final place~\cite{wikibubble}. +With this in mind, we write our final implementation: +\begin{verbatim} +// ... +public class Sort +{ + // ... + public static > + void bubble(List list) + { + for (int n = list.size(), m = 0; n > 1; n = m, m = 0) + for (int i = 1; i < n; ++i) + if (list.get(i).compareTo(list.get(i - 1)) < 0) + swap(list, m = i, i - 1); + } +} +\end{verbatim} + +In the best case where the list is already sorted, during the first iteration +of the outer loop, \verb|m| remains zero, which is then assigned to \verb|n| +to break the loop, hence the time complexity is that of the inner loop +or $n - 1 = \Omega(n)$. Otherwise, denote $S$ as a subset of all +positive integers not greater than \verb|list.size()|. The time complexity +would then be +\[T(n) = \sum_{n\in S}\Theta(n)\] +On random data, $\#S = \Theta(n)$ and thus $T(n) = \Theta(n^2)$. + +As demonstrated in~\cite{bubblebad}, selection sort outperforms bubble sort +on average cage, despite their asymptotically equivalent time complexity: +\begin{center} + \includegraphics[width=0.57\textwidth]{bubbleplot.jpg} +\end{center} + +Looking back at~\cite[p. 646]{wu}, we are now able to spot Wu's misconception: +\begin{quote} + On average, we expect the bubble sort to finish sorting sooner than + the selection sort, \emph{because there will be more data movements for + the same number of comparisons}, and there is a test to exit the method + when the array gets sorted. +\end{quote} + +While bubble sort may take advantage of the pre-sorted parts of the array +to do fewer comparisons, the number of swaps is $\Theta(n^2)$, which grows +strictly faster than selection sort's $\Theta(n)$. Consequently, instead of +swapping more to finish faster, the bubble variant does more unnecessary +data movements. Furthermore, write operations on memory are usually slower +than reading, which makes swaps much costlier than comparisons. Finally, +it could be summarized by a quote from Donald Knuth~\cite{peculiar}, +\begin{quote} + In fact, one of Demuth's main results was that in a certain sense + `bubble sorting' is the optimum way to sort. [...] It turns out that + [all other sorting methods studied] are always better in practice, + in spite of the fact that Demuth has proved the optimality of bubble sorting + on a certain peculiar type of machine. +\end{quote} + +\subsection{Heapsort} +Heapsort could be viewed as an improved version of selection sort, where +the selection is done on the right data structure: the heap~\cite{heapselect}. +To sort an array ascendingly in place, we use a binary max-heap to keep track +of the largest element in the unsorted region and move it to the beginning +of the sorted one, iteratively. + +The binary heap is an array object $A$ which resembles a nearly completely +filled binary tree, except possibly the lowest level. This kind of heap has +two attributes: $length$ of the array and $size$ of the heap, +where $0 \le size \le length$ and valid elements of the heap only reside +within the $[0, size)$ index interval of the array~\cite[p. 151]{clrs}. + +We choose \verb|java.util.List| as the interface for the inner representation +of heap and come up with the following declaration: +\begin{verbatim} +import java.util.List; + +public class Heap> +{ + private List list; + private int size; + public int getSize() + { + return size; + } + + public int getLength() + { + return list.size(); + } + + public T get(int i) + { + return list.get(i); + } +} +\end{verbatim} + +Since the elements are of the type \verb|Comparable|, \verb|Heap| is a max-heap +in a purely conceptual sense. That is, the order totally depends on +\verb|T.compareTo|, which could be defined or overridden by the users. +The max-heap property is then defined as that for every node $i$ other than +the root $A_0$, $A_{\mathrm{parent}(i)} \ge A_i$, where the indices of a node's +parent, left child and right child are given by +\[\begin{array}{ll} + \mathrm{parent}(i) &= \left\lfloor\dfrac{i - 1}{2}\right\rfloor\\ + \mathrm{left}(i) &= 2i + 1\\ + \mathrm{right}(i) &= 2i + 2 +\end{array}\] + +To maintain this property at node $i$, with the assumption that +the binary trees rooted at left($i$) and right($i$) are already max-heaps, +we use \verb|heapify|, which sift the value at $A_i$ down the heap if it is +smaller than its children: +\begin{verbatim} +// ... +import static java.util.Collections.swap; + +public class Heap> +{ + // ... + public void heapify(int i) + { + int right = i + 1 << 1; + int left = right - 1; + int largest = i; + if (left < size && get(left).compareTo(get(largest)) > 0) + largest = left; + if (right < size && get(right).compareTo(get(largest)) > 0) + largest = right; + if (largest != i) + { + swap(list, i, largest); + heapify(largest); + } + } +} +\end{verbatim} + +At each call, the index of the greatest element among $A_i$, +$A_{\mathrm{left}(i)}$ and $A_{\mathrm{right}(i)}$ is assigned +to \verb|largest|. In case $A_i$ is greatest, the subtree rooted at $A_i$ +is already a max-heap and the method terminates. Otherwise, $A_i$ is swapped +with $A_{\mathtt{largest}}$, which makes node $i$ and its children to satisfy +the max-heap property. The subtree rooted at \verb|largest|, though, +\emph{might} violate the property so \verb|heapify| needs to be called on it. +For instance, given $i$ points to the subtree on the left, then \verb|heapify| +would step-by-step turn it to the one on the right: +\begin{center} + \begin{forest} + for tree={circle,draw} + [,phantom [4,fill=yellow [9 [0] [6]] [8 [3] [,phantom]]] + [9 [4,fill=yellow [0] [6]] [8 [3] [,phantom]]] + [9 [6 [0] [4,fill=yellow]] [8 [3] [,phantom]]]] + \end{forest} +\end{center} + +The running time of \verb|heapify| on a subtree of height (number of edges +between the root and the furthest leaf) $h$ is the time $\Theta(1)$) to make +$A_i$, $A_{\mathrm{left}(i)}$ and $A_{\mathrm{right}(i)}$ a max-heap, plus +the time to run \verb|heapify| on one of its children if necessary: +\[T(h) \le T(h - 1) + \Theta(1) + \le T(0) + \sum_{i=1}^{h}\Theta(1) = T(0) + \Theta(h)\] +Since $T(0)$ is obviously $\Theta(1)$, +\[T(h) \leq \Theta(h) \Longrightarrow T(h) = O(h)\footnotemark\] +A binary tree of size $n$ may have at maximum height of +$h = \lfloor\log_2 n\rfloor$, thus the time complexity with respect to +the size of the subtree is $O(\log_2 n)$. +\footnotetext{The notation $f = O(g)$ indicates that $f$ grows +at most as as fast as $g$.} + +Noticeably, $\forall i \ge \lfloor n/2\rfloor,\; 2i + 2 > 2i + 1 \ge n +\iff \mathrm{right}(i) > \mathrm{left}(i) \ge n$. In other words, +only nodes of indices less than $\lfloor n/2\rfloor$ have children. +Therefore, a heap could be constructed as follows +\begin{verbatim} +// ... +public class Heap> +{ + // ... + public Heap(List a) + { + list = a; + size = a.size(); + for (int i = size >> 1; i-- > 0;) + heapify(i); + } +} +\end{verbatim} + +Before the construction, each node from $\lfloor n/2\rfloor$ (\verb|size >> 2|) +to $n - 1$ is a leaf and a trivial heap on its own. Consider the iteration +where \verb|heapify| is called on node $i$ and assume every node after $i$ +is the root of a heap beforehand, then after the iteration, all nodes from $i$ +to $n - 1$ are foots of max-heaps. Consequently, by mathematical induction, +after the construction, the whole array becomes a heap. According +to~\cite[p. 159]{clrs}, this constructor runs in linear time ($O(n)$). + +Heapsort uses the max-heap to select the next largest element to move it +to the start of the sorted region. This in-place movement could be implemented +as the method \verb|pop|, which also return the largest element to make +the method make sense on its own. +\begin{verbatim} +// ... +public class Heap> +{ + // ... + public T pop() throws RuntimeException + { + if (size < 1) + throw new RuntimeException("heap underflow"); + swap(list, 0, --size); + heapify(0); + return get(size); + } +} +\end{verbatim} + +Choosing the $n - 1$ largest element, we are left with the minimum value at +the beginning of the list and the rest of the list are all greater or equal to +the first element. +\begin{verbatim} +// ... +public class Sort +{ + // ... + public static > + void heap(List list) + { + var heap = new Heap(list); + for (int i = 1; i < list.size(); ++i) + heap.pop(); + } +} +\end{verbatim} + +The step-by-step selection from the example heap is shown below as +an illustration. One may notice that the result qualifies as a min-heap. +\begin{center} + \begin{forest} + for tree={circle,draw} + [,phantom [9 [6 [0] [4]] [8 [3] [,phantom]]] + [8 [6 [0] [4]] [3 [9,edge=dotted] [,phantom]]] + [6 [4 [0] [8,edge=dotted]] [3 [9,edge=dotted] [,phantom]]]] + \end{forest} +\end{center} +\begin{center} + \begin{forest} + for tree={circle,draw} + [,phantom [4 [0 [6,edge=dotted] [8,edge=dotted]] + [3 [9,edge=dotted] [,phantom]]] + [3 [0 [6,edge=dotted] [8,edge=dotted]] + [4,edge=dotted [9,edge=dotted] [,phantom]]] + [0 [3,edge=dotted [6,edge=dotted] [8,edge=dotted]] + [4,edge=dotted [9,edge=dotted] [,phantom]]]] + \end{forest} +\end{center} + +The running time of \verb|Sort.heap| is that of \verb|Heap| constructor, +plus $n - 1$ times that of \verb|Heap.pop|, which is +\begin{align*} + T(n) &= O(n) + (n - 1)(\Theta(1) + O(\log_2 n))\\ + &= O(n) + \Theta(n)O(\log_2 n)\\ + &= O(n) + O(n\log_2 n)\\ + &= O(n\log_2 n) +\end{align*} +This is a huge improvement from selection sort's and bubble sort's $O(n^2)$. +\pagebreak + +\section{Comparing}\label{sec:cmp} +Our implementations of searching and sorting algorithms in Java work on +any collection of data whose \emph{natural} order is specified, that is, +where elements implement \verb|Comparable|. However, they does not cover +the case \emph{another} order is required, there is not any option to use +our current library other than subclassing every item with a different +\verb|compareTo| method, which, in every sense, is counter-intuitive. + +In~\cite{wu}, Wu dedicated a whole ``Sample Development'' section to address +this issue and came up with the final solution of using the standard interface +\verb|java.util.Comparator|~\cite{cmp} which declare the method \verb|compare|. +We start with refactoring \verb|Sort.selection| to comply with this facility: +\begin{verbatim} +// ... +import java.util.Comparator; + +public class Sort +{ + public static + void selection(List list, Comparator comparator) + { + int i, j, m, n = list.size(); + for (i = 0; i < n; ++i) + { + for (m = j = i; j < n; ++j) + if (comparator.compare(list.get(j), list.get(m)) < 0) + m = j; + swap(list, i, m); + } + } +} +\end{verbatim} + +To sort in reversed order, we can subclass \verb|Comparator| +anonymously~\cite{anon} +\begin{verbatim} +var list = java.util.Arrays.asList(4, 2, 0, 6, 9); +Sort.selection(list, new java.util.Comparator() +{ + public int compare(Integer a, Integer b) + { + return -a.compareTo(b); + } +}); +System.out.println(list); +\end{verbatim} + +As expected, the snippet above prints \verb|[9, 6, 4, 2, 0]| to \verb|stdout|. + +In order to maintain backward-comparability, we write a helper class extracting +the natural order from any \verb|Comparable| to a \verb|Comparator|: +\begin{verbatim} +import java.util.Comparator; + +public class Compare> +implements Comparator +{ + public int compare(T a, T b) + { + return a.compareTo(b); + } +} +\end{verbatim} +and wrap the current \verb|Sort.selection| with +\begin{verbatim} +// ... +public class Sort +{ + // ... + public static > + void selection(List list) + { + selection(list, new Compare()); + } +} +\end{verbatim} + +\verb|Sort.bubble| can be refactored similarly, but since \verb|Sort.heap| +does all the comparisons in \verb|Heap|, we are required to make \verb|Heap| +work with \verb|Comparator|'s +\begin{verbatim} +// ... +import java.util.Comparator; + +public class Heap +{ + // ... + private Comparator cmp; + + public void heapify(int i) + { + int right = i + 1 << 1; + int left = right - 1; + int largest = i; + if (left < size && cmp.compare(get(left), get(largest)) > 0) + largest = left; + if (right < size && cmp.compare(get(right), get(largest)) > 0) + largest = right; + if (largest != i) + { + swap(list, i, largest); + heapify(largest); + } + } + + public Heap(List a, Comparator c) + { + list = a; + size = a.size(); + cmp = c; + for (int i = size >> 1; i-- > 0;) + heapify(i); + } +} +\end{verbatim} +and re-write \verb|Sort.heap| to use the new \verb|Heap| +\begin{verbatim} +// ... +public class Sort +{ + // ... + public static + void heap(List list, Comparator comparator) + { + var heap = new Heap(list, comparator); + for (int i = 1; i < list.size(); ++i) + heap.pop(); + } + + public static > + void heap(List list) + { + heap(list, new Compare()); + } +} +\end{verbatim} + +For the next example, we use a minimal, read-only version of the \verb|Person| +class from~\cite[p. 666]{wu}, which is ordered by \verb|name| by default: +\begin{verbatim} +public class Person implements Comparable +{ + private String name; + private Integer age; + private Character gender; + + public Person(String name, Integer age, Character gender) + { + this.name = name; + this.age = age; + this.gender = gender; + } + + public int compareTo(Person other) + { + return this.name.compareTo(other.name); + } + + public String toString() + { + return String.format("%s (%d%c)", name, age, gender); + } + + public String getName() + { + return name; + } + + public Integer getAge() + { + return age; + } + + public Character getGender() + { + return gender; + } +} +\end{verbatim} + +We use a list of the oldest current state leaders~\cite{old} for this test +\begin{verbatim} +var list = java.util.Arrays.asList( + new Person("Mahathir Mohamad", 94, 'M'), + new Person("Elizabeth II", 93, 'F'), + new Person("Sheikh Sabah Al-Ahmad Al-Jaber Al-Sabah", 90, 'M'), + new Person("Paul Biya", 86, 'M'), + new Person("Michel Aoun", 84, 'M'), + new Person("Mahmoud Abbas", 83, 'M'), + new Person("Francis", 82, 'M')); +Sort.heap(list); +list.forEach(System.out::println); +\end{verbatim} +and get +\begin{verbatim} +Elizabeth II (93F) +Francis (82M) +Mahathir Mohamad (94M) +Mahmoud Abbas (83M) +Michel Aoun (84M) +Paul Biya (86M) +Sheikh Sabah Al-Ahmad Al-Jaber Al-Sabah (90M) +\end{verbatim} + +It would neither be challenging to re-sort this list by their ages: +\begin{verbatim} +var ageComparator = new java.util.Comparator() +{ + public int compare(Person a, Person b) + { + return a.getAge().compareTo(b.getAge()); + } +}; +Sort.heap(list, ageComparator); +list.forEach(System.out::println); +\end{verbatim} +which gives us +\begin{verbatim} +Francis (82M) +Mahmoud Abbas (83M) +Michel Aoun (84M) +Paul Biya (86M) +Sheikh Sabah Al-Ahmad Al-Jaber Al-Sabah (90M) +Elizabeth II (93F) +Mahathir Mohamad (94M) +\end{verbatim} + +Linear search is able to run without any modification: we would still receive +4 from \verb|Search.linear(list, list.get(4))|. Binary search, however, +would not work correctly if the order of the list is not \emph{natural}. +One with \verb|Comparator| support may be implemented as follows. +\begin{verbatim} +import java.util.List; +import java.util.Comparator; + +public class Search +{ + // ... + private static + int binary(List list, T key, + Comparator c, int low, int high) + { + if (high < low) + return NOT_FOUND; + var mid = (low + high) / 2; + var cmp = c.compare(list.get(mid), key); + if (cmp < 0) + return binary(list, key, c, mid + 1, high); + if (cmp > 0) + return binary(list, key, c, low, mid - 1); + return mid; + } + + public static + int binary(List list, T key, + Comparator c) + { + return binary(list, key, c, 0, list.size()); + } +} +\end{verbatim} + +Now \verb|Search.binary(list, list.get(5), ageComparator)| would return +the right index 5 instead of -1 like \verb|Search.binary(list, list.get(5))|. +\pagebreak + +\section{Conclusion} +Albeit searching and sorting belong to the category of algorithms, +by implementing them, we were still able to recognize a variety of +object-oriented concepts, namely: +\begin{enumerate} + \item Through encapsulation of the inner representations + and behaviors, we can create intuitive programming interfaces, + whilst keeping the code clear and concise. Notable examples are + the implementation of binary search and the heap data structure. + \item Through polymorphism, generic and convenient libraries can be written, + which allow more \emph{code reuse} and thus facilitate more effective + development. We wrote all of the classes and methods with this principle + in mind. + \item Through inheritance, it is possible to extend the functionalities of + each object. Together with polymorphism (which defines the behavioral + interfaces), it enable us to generalize our codebase one step further, + as shown in section~\ref{sec:cmp}, without spending too much energy + on refactoring. +\end{enumerate} + +Generally, though, we find shoving every self-contained function into a class +as a static method rather redundant. +\pagebreak + +\begin{thebibliography}{99} + \bibitem{wu} C. Thomas Wu. + ``Chapter 11: Sorting and Searching''. + \emph{An Introduction to Object-Oriented Programming with Java}, 5 ed. + McGraw-Hill, 2010. ISBN 978–0–07–352330–9. + \bibitem{list} \href{https://docs.oracle.com/javase/8/docs/api/java/util/List.html}{List (Java Platform SE 8)}. + \emph{Oracle}. + \bibitem{var} \href{http://openjdk.java.net/jeps/286}{JEP 286: Local-Variable Type Inference}. + \emph{OpenJDK}. + \bibitem{bsearch} \href{http://mathworld.wolfram.com/BinarySearch.html}{Binary Search}. + \emph{Wolfram WathWorld}. + \bibitem{infer} \href{https://docs.oracle.com/javase/tutorial/java/generics/methods.html}{Generic Methods}. + \emph{Oracle}. + \bibitem{collections} \href{https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html}{Collections (Java Platform SE 8)}. + \emph{Oracle}. + \bibitem{clrs} Thomas H. Cormen, Charles E. Leiserson, + Ronald L. Rivest and Clifford Stein. + \emph{Introduction to Algorithms}, 3 ed. + MIT Press, 2009. ISBN 978-0-262-03384-8. + \bibitem{bubblebad} Owen Astrachan. + \emph{\href{https://users.cs.duke.edu/~ola/bubble/bubble.html}{Bubble Sort: An Archaeological Algorithmic Analysis}}. + Duke University. + \bibitem{wikibubble} \href{https://en.wikipedia.org/wiki/Bubble_sort#Optimizing_bubble_sort}{Bubble sort}. + \emph{Wikipedia}. + Section 2.2: \emph{Optimizing bubble sort}. + \bibitem{peculiar} Donald E. Knuth. The Dangers of Computer-Science Theory. + \emph{Studies in Logic and the Foundations of Mathematics}, vol. 74. + Elsevier, 1973. + doi:\href{https://doi.org/10.1016/S0049-237X(09)70357-X}{10.1016/S0049-237X(09)70357-X}. + \bibitem{heapselect} Steven Skiena. + ``Searching and Sorting''. + \emph{The Algorithm Design Manual}, p. 109. + Springer, 2008. + doi:\href{https://doi.org/10.1007/978-1-84800-070-4_4}{10.1007/978-1-84800-070-4{\_}4}. + ISBN 978-1-84800-069-8. + \bibitem{cmp} \href{https://docs.oracle.com/javase/8/docs/api/java/util/mparator.html}{Comparator (Java Platform SE 8)}. + \emph{Oracle}. + \bibitem{anon} \href{https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html}{Anonymous Classes}. + \emph{Oracle}. + \bibitem{old} \href{https://en.wikipedia.org/wiki/Lists_of_state_leaders_by_age#10_oldest_currently_serving_state_leaders}{Lists of state leaders by age}. + \emph{Wikipedia}. + Section 1: \emph{10 oldest currently serving state leaders}. +\end{thebibliography} +\end{document} -- cgit 1.4.1