\part{Solutions}
\setcounter{chapter}{0}

\ifthenelse{\boolean{createspace} }{
}
{
\renewcommand{\chaptermark}[1]{
\ifthenelse{\boolean{kindle1}}{\makeoddhead{kindle1page}{\scriptsize{Ch.~\arabic{chapter}}}{\scriptsize \em Solutions}{\scriptsize #1} }{}
}
}

\chapter{ Searching}\normalsize

\ans{sqrt}
One of the fastest ways to invert a fast-growing monotone function (such as the square function)
is to do a binary search in a precomputed table of the function.
Since the square root for the largest 32-bit unsigned integer can be represented in 16 bits, we build an array of
length $2^{16}$ such that $i$-th element in the array is $i^2$. When
we want to compute square root for a given number $n$, we look for the
largest number in the array that is still smaller than $n$.  Because
the square root is relatively small, it is faster to compute it on the fly
than to precompute it.
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
unsigned int sqrt_search(unsigned int input) {
  int begin = 0;
  int end = 65536;
  while(begin + 1 < end){
    int mid = begin + (end - begin) / 2;
      unsigned int mid_sqr = mid * mid;
    if (mid_sqr == input) {
      return mid;
    } else if (mid_sqr > input) {
      end = mid;
    } else {
      begin = mid;
    }
  }
  return begin;
}
\end{lstlisting}

\ans{bin-search}
% We implemented binary search through an array in Problem~\ref{sqrt}.
% from file BinSearch.java
% run as javac BinSearch.java ; java -ea -cp . BinSearch
\lstinputlisting[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]{BinSearch.java}

\ans{bin-search-next}
A straightforward way to find an element larger than
a given value $k$ is to look for $k$ via a
binary search and then, if $k$ is found, walk the array
forward (linearly) until either the first element
larger than $k$ is encountered or the end of the array is reached.
If $k$ is not found, a binary search will end up pointing to
either the next largest value after $K$ in the array, in which case no further action is required or the
next smallest value in which case the next element is the next largest value.

\begin{comment}
Here is an implementation in Python:
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Python]
import math

# Find first element larger than k in sorted array
def firstElementLargerThanK(arr, k):
    # use binary search to find position of k or neighbor if not found
    top = len(arr) - 1
    bot = 0
    foundK = False
    while (bot <= top and foundK == False):
        mid = bot + ((top - bot + 1) / 2)
        if arr[mid] < k:
            bot = mid + 1
        if arr[mid] > k:
            top = mid - 1
        if arr[mid] == k:
            foundK = True

    # if neighbor is greater, we are done
    if arr[mid] > k:
        return mid, arr[mid]
    else:
        for i in range(mid + 1, len(arr)):
            if arr[i] > k:
                return i, arr[i]
        return 0, 0
\end{lstlisting}

Here are the test runs:

{\footnotesize
\begin{verbatim}
a1 = [6, 23, 22, 81, 4, 2, 5, 5, 10, 9, 47]
a1.sort()
firstElementLargerThanK(a1, 21) = (7, 22)

a2 = [6, 23, 22, 81, 21, 2, 5, 5, 10, 9, 47]
a2.sort()
firstElementLargerThanK(a2, 21) = (7, 22)

a3 = [6, 23, 22, 81, 4, 2, 5, 5, 36, 9, 47]
a3.sort()
firstElementLargerThanK(a3, 21) = (6, 22)

a4 = [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6]
a4.sort()
firstElementLargerThanK(a4, 5) = (12, 6)
\end{verbatim}
}
\end{comment}

The worst-case runtime of this algorithm is $\Theta(n)$---the
input of all values matching $K$, except for
the last one (which is greater than $K$), is the worst-case.
%Imagine if it were one million values of 5 instead of 20---then
%our original algorithm would inspect approximately 500,000 elements
%instead of $\log_2(500000) \approx 19$ elements.

The solution to this problem is to replace the linear scan with a binary search
in the second part of the algorithm, which
leads to the desired element to be found in $O(\log n)$ time.

\begin{comment}
The problem with this, of course, is that the worst-case input may not be
the ``typical'' input. If we expect a uniform random distribution of values from a large range,
the expected time to reach the next larger element after having found the position for $K$
would be constant, i.e., we would expect to find the next biggest element in a
small number of steps, regardless of $n$. In the case of a million unique randomly distributed sorted values, our original
algorithm would solve the first part of the problem in $\log_2(1,000,000) = 20$ steps and then
exactly one step for the second part, compared to another 19 steps for the ``improved''
algorithm that used a second binary search.
\end{comment}

\begin{comment}
On the other hand, if there are many expected duplicates, there is a crossover point after
which the binary search does become faster. Specifically, that number is $\log_2\frac{n}{2})$; so, in
the case of $1,000,000$, if we expect any more than 19 copies of each element in the sorted
array, we are still better off using a second binary search to find the next highest
number rather than a linear walk through 19 elements. Of course, the constant factors in
the algorithm will have an impact as well, but which algorithm is better still depends on the amount of duplication relative to $n$.
\end{comment}

\ans{bin-search-equal}
Since the array contains
distinct integers and is sorted, for any $i >0$, $A[i] \ge A[i-1] +1$.
Therefore $B[i] = A[i] -i$ is also nondecreasing. It follows that
we can do a binary search for 0 in $B$ to find an
index such that $A[i] = i$.
(We do not need to actually create $B$, we can simply use $A[i] -i$ wherever $B[i]$ is referenced.)

\ans{bin-search-unknown-length}
The key idea here is to simultaneously do a binary search for the end
of the array as well as the key.
We try to look for $A[2^k]$ in the $k$-th step and
catch exceptions for successive values of $k$ till either we hit an
exception or we hit a number greater than or equal to $b$. Then we do a
binary search for $b$ between indices $2^{k-1}$ and $2^k$.
The runtime of the search algorithm is $O(\log n)$.
In code:
%tested
 \begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
int BinarySearchInUnboundedArray(int * A, int b) {
  int k = 0;
  while(true) {
    int c;
    try {
      c = A[(1 << k) -1];
      if (c == b) {
        return (1 << k) -1;
      } else if (c >= b) {
        break;
      }
    }
    catch (exception e) {
      break;
    }
    k++;
  }
  // Now do a binary search between indices 2^(k-1) and (2^k)-1.
  int begin = 1 << (k -1);
  int end = (1 << k) - 1;
  while(begin + 1 > end) {
    int mid = begin + (end - begin) / 2;
    try {
      if (A[mid] == b) {
        return mid;
      } else if (A[mid] < b){
        begin = mid;
      } else {
        end = mid;
      }
    }
    catch (exception e) {
      end = mid;
    }
  }
  // Nothing matched b
  return -1;
}
 \end{lstlisting}

\ans{missing-small-ram}
In the first step, we build an array of
$2^{16}$ integers that is initialized to $0$ and for every number in the file,
we take its $16$ most significant bit to index into this array and
increment that number. Since there are less than $2^{32}$ numbers in the
file, there is bound to be one number in the array that is less
than $2^{16}$. This tells us that there is at least one number missing
among the possible numbers with those upper bits. In the second pass,
we can focus only on the numbers that match this criterion and use a bit-vector of size $2^{16}$ to identify one of the missing
numbers.

\begin{comment}

Ian's solution

When the two arrays are of comparable size, the intersection can be
computed by walking both the arrays simultaneously like merge sort and
only write out the entries that are found in both the arrays. This can
be done in $O(n + m)$ time.

When $m$ is much smaller than $n$, it can be more efficient to do a
binary search for each of the elements of $B$ in $A$ and write out the
elements that were found. This can be done in $O(m \log n)$ time.

Given sorted arrays $A$ and $B$ of length $n$ and $m$ respectively, return an array $C$
containing elements common to $A$ and $B$. The array $C$ should be free of duplicates. How would you
perform this intersection if $n$ and $m$ were roughly equal? How about
if $n$ is much less than $m$?
\end{comment}

\ans{intersection}
% This problem is quite commonly solved by databases while performing join operations.
% However the task of databases is more difficult since  it has to deal with the difference in access time for secondary storage.
The simplest algorithm is a ``loop join'', i.e., walking through
all the elements of one array and comparing them to the elements of the other array. This has
$O(m \cdot n)$ time complexity, regardless of whether the arrays are sorted or unsorted:

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Python]
for each unique element in A
    for each unique element in B
        if A = B
            include A in output
\end{lstlisting}

However since both the arrays are sorted, we can
make some optimizations. First, in the right array, we can
use binary search to find whether the element
exists rather than scanning the entire array:

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Python]
for each unique element in A
    use binary search to find A in B
    if found, include A in output
\end{lstlisting}

Now our algorithm should be $O(n \cdot \log_2 m )$. We should
choose the larger set for the inner
loop (i.e., binary search) since if $n \ll m$ then $m\log(n) \gg n\log(m)$.

\begin{comment}
For example:

If $n = 1000$ and $m = 100$, we have $1000 \cdot \log_2(100) \approx 6643$
If $n = 100$ and $m = 1000$, we have $100 \cdot \log_2(1000) \approx 997$

If $n = 600$ and $m = 500$, we have $600 \cdot \log_2(500) \approx 5379$
If $n = 500$ and $m = 600$, we have $500 \cdot \log_2(600) \approx 4614$

If $n = 1000$ and $m = 999$, we have $1000 \cdot \log_2(999) \approx 9964$
If $n = 500$ and $m = 600$, we have $500 \cdot \log_2(600) \approx 9955$
\end{comment}

This is the best solution if one set is much smaller than
the other. However it is not optimal for cases where the set sizes are similar because
we are not using the fact that both arrays are sorted to our advantage.
In that case, a linear scan through both the arrays in tandem will work
best as shown in this Python code:

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Python]
def TryLinearIntersect(n, m, a, b):
    # construct sorted sets of random numbers of size n and m
    A = []
    for i in range(n):
        A.append(random.randint(a, b))
    A.sort()

    B = []
    for j in range(m):
        B.append(random.randint(a, b))
    B.sort()

    return LinearIntersect(A, B)

def LinearIntersect(A, B):
    output = []
    ACounter = 0
    BCounter = 0
    lastMatch = None
    while ACounter < len(A) and BCounter < len(B):
        if A[ACounter] == B[BCounter] and A[ACounter] != lastMatch:
            lastMatch = A[ACounter]
            output.append(lastMatch)
            ACounter = ACounter + 1
            BCounter = BCounter + 1
        elif A[ACounter] < B[BCounter]:
            ACounter = ACounter + 1
        else:
            BCounter = BCounter + 1
    return output
\end{lstlisting}

\begin{comment}
Some example runs:

\begin{verbatim}
>> TryLinearIntersect(100, 100, 1, 100)

A: [1, 3, 4, 4, 7, 8, 10, 12, 12, 12, 14, 14, 14, 16, 16,
	17, 21, 21, 22, 22, 23, 23, 23, 24, 24, 26, 27, 28,
	28, 29, 31, 31, 32, 32, 33, 33, 33, 35, 37, 37, 38,
	38, 38, 40, 40, 41, 44, 44, 44, 45, 45, 46, 46, 47,
	49, 49, 52, 52, 53, 53, 54, 54, 54, 55, 57, 58, 60,
	61, 61, 63, 63, 65, 66, 71, 72, 73, 75, 76, 78, 79,
	79, 81, 81, 82, 85, 87, 87, 88, 88, 88, 88, 89, 90,
	92, 93, 93, 94, 95, 96, 98]

B: [2, 2, 2, 3, 4, 4, 4, 5, 9, 9, 10, 10, 13, 13, 14, 16,
	17, 17, 19, 21, 22, 26, 26, 26, 27, 28, 30, 30, 30,
	30, 31, 32, 33, 34, 34, 37, 37, 39, 40, 45, 46, 46,
	46, 48, 49, 50, 53, 55, 56, 58, 59, 59, 59, 60, 62,
	62, 62, 63, 66, 67, 69, 69, 74, 74, 74, 75, 77, 78,
	79, 79, 79, 79, 79, 81, 81, 81, 81, 81, 82, 83, 84,
	84, 86, 87, 88, 90, 90, 91, 92, 96, 97, 97, 97, 98,
	98, 98, 98, 99, 100, 100]

Output: [3, 4, 10, 14, 16, 17, 21, 22, 26, 27, 28, 31, 32,
		33, 37, 40, 45, 46, 49, 53, 55, 58, 60, 63,
		66, 75, 78, 79, 81, 82, 87, 88, 90, 92, 96, 98]
\end{verbatim}
\end{comment}

The runtime for this algorithm is $O(m + n)$.

\begin{comment}
There is one additional case where we can improve on this; if there are expected to be long
runs of nonmatching values in between matches, then we can improve our time by using
binary search on either set, compared to the most recent value in the other set. This will
be advantageous if the log of the number of remaining values is smaller than the
nonmatching value run-length.
\end{comment}
\begin{comment}
\begin{verbatim}
def TryLinearIntersectWithBSLookahead(n, m, a, b):
    # construct sorted sets of random numbers of size n and m
    A = []
    for i in range(n):
        A.append(random.randint(a, b))
    A.sort()

    B = []
    for j in range(m):
        B.append(random.randint(a, b))
    B.sort()

    return LinearIntersectWithBSLookahead(A, B)

def LinearIntersectWithBSLookahead(A, B):
    #editor.AddText("\nOriginal: A: " + str(A) + "; B: " + str(B) + "\n")
    output = []
    ACounter = 0
    BCounter = 0
    lastMatch = None
    jumpout = 0
    while ACounter < len(A) and BCounter < len(B): #and jumpout < 1000:
        if A[ACounter] == B[BCounter] and A[ACounter] != lastMatch:
            #editor.AddText("A[ACounter] = " + str(A[ACounter]) + ", B[BCounter] = " + str(B[BCounter]) + ", lastMatch = " + str(lastMatch) + "\n")
            lastMatch = A[ACounter]
            output.append(lastMatch)
            ACounter = ACounter + 1
            BCounter = BCounter + 1
        elif A[ACounter] < B[BCounter]:
            ACounter = BS(A, ACounter + 1, B[BCounter])
        else:
            BCounter = BS(B, BCounter + 1, A[ACounter])
        #jumpout = jumpout + 1
        #editor.AddText("\output " + str(jumpout) + ": " + str(output) + "\n")
    return output

# return the position of the array where the value is or would be
def BS(ar, val, low):
    high = len(ar)
    while low < high:
        mid = (low + high) // 2
        midval = ar[mid]
        if midval < val:
            low = mid+1
        elif midval > val:
            high = mid
        else:
            return mid
    return low
\end{verbatim}

We should  be able to tell the timing difference in large arrays with
sparse overlap---say, 10000 items in each array drawn from a set
of 1000000 integers.

\begin{verbatim}
>> TryLinearIntersect(10000, 10000, 1, 1000000)
TryLinearIntersect took 187.00003624 ms

>> TryLinearIntersectWithBSLookahead(10000, 10000, 1, 1000000)
TryLinearIntersectWithBSLookahead took 92.9999351501 ms
\end{verbatim}
\end{comment}

\ans{anagrams}
A simple way to approach this problem is to hash each word based on its sorted
representation (i.e., ``logarithm'' and ``algorithm'' would both be hashed as ``aghilmort'').
This ensures that all the anagrams of a given word map to the same hash value.

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Python]
def anagrams(dictionary):
    output = []
    map = {}
    # for each word
    for word in dictionary:
        # sort the letters
        sorted_word = sortchars(word)
        # add the word to the list held in a dictionary
        # under its sorted key
        if sorted_word not in map:
            map[sorted_word] = [word]
        else:
            map[sorted_word].append(word)
    # for each dictionary key
    for k in map.keys():
        # return the list if it has more than one item
        if len(map[k]) > 1:
            output.append(map[k])
    return output

def sortchars(word):
    l = list(word)
    l.sort()
    return ''.join(l)
\end{lstlisting}

A sample run:
{\footnotesize
\begin{verbatim}
>> anagrams(("algorithm", "god", "logarithm", "dog", "snute"))
    [['algorithm', 'logarithm'], ['god', 'dog']]
\end{verbatim}
}

\ans{search-sum}
This could be easily done in $O(n^2)$ time by searching for all possible values of $i$ and $j$ such that $A[i] + A[j] = K$.

We could do significantly better by storing the values from the array in
a hash table. Then for each new value, we check to see if its
complement (i.e., $K$ minus the value) has already
been seen and if so, what is the index?
Here is a Python implementation of this
concept using Python's built-in dictionary object as the hash table:

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Python]
def PairSum(arr, K):
    h = {}
    for i in range(len(arr)):
        complement = K - arr[i]
        h[arr[i]] = i
        if complement in h:
            return h[complement], i
\end{lstlisting}

This gives the following results, where the return values of the function are the two
indices of elements that add up to $K$:
{\footnotesize
\begin{verbatim}
ar1 = [2, 3, 4, 5, 6, 7, 8, 7]

PairSum(ar1, 4) = (0, 0)
PairSum(ar1, 5) = (0, 1)
PairSum(ar1, 10) = (3, 3)
PairSum(ar1, 13) = (4, 5)
PairSum(ar1, 15) = (5, 6)
PairSum(ar1, 17) = None
\end{verbatim}
}

This algorithm runs in $O(n)$ time since it makes only a single pass through the list
and the work done inside the loop is constant (assuming we have
a nice hash function that gives us a constant time hash insert and lookup).

\ans{anonymous-letter}
Here essentially we need to efficiently represent two multisets (one
for characters in the anonymous letter and one for characters in the
magazine) and see if one is a subset of the other.

The most direct way of doing this would be to build a hash table $M$, where
the key is a character and its value is the number of times it appears
in the magazine.  Once this is built, we can scan the anonymous letter
character by character and decrement the corresponding count in $M$.
If the count goes to zero, we delete the character from $M$.
We can write the anonymous letter with characters in the magazine
iff we can go over the entire anonymous letter and find every
character in $M$ with a positive count.

If the characters are coded in ASCII, we could do away with $M$ and use a 256 entry
integer array $A$, with $A[i]$ being set to the number of times the character $i$ appears
in the magazine.

One way to improve performance of the approach outlined above
when the magazine is very long is to process the magazine in segments;
in this way, if the letter can be written with a relatively
small initial prefix of the magazine, the whole magazine does not have
to be processed.
The segments may be of fixed size or a doubling strategy may be employed.
This does not help the worst-case complexity (since
it may not be possible to write the letter with the characters in the magazine
and this cannot be determined without inspecting the entire magazine) but
speeds up the best-case and possibly the average-case.

\ans{pair-users}
Here essentially each user is associated with a set of attributes and
we need to find users associated with a given set of attributes
quickly. A hash table would be a perfect solution here but we need
a hash function over the set of attributes.  There are a couple of
good ways of doing this. If the number of attributes is small, then we
can represent the set as a bit-vector, where each bit represents a
specific attribute. Once we have this canonical representation of set,
then it is easy to use any hash function that transforms this bit-vector into a desired hash space.

However if the space of possible attributes is large, then the best way
to represent a set canonically would be to sort the attributes. For this sorting,
any arbitrary ordering of attributes will work. We can represent the sorted
list of attributes in a string concatenating all the attributes.

Incidentally, if we want to group users based on similar rather than identical attributes, the
problem becomes significantly more difficult. A common approach is
min-hashing. Essentially, we construct a set of $k$ independent hash functions
($k$ is chosen based on how similar we want the sets to be). Then for
each set $s$ we define
\[M_k(s) = \min_{a_i \in s} h_k(a_i) . \]
If two sets $s_1$ and $s_2$ have similar set of attributes then with
high probability $M_k(s_1) = M_k(s_2)$. Based on this, we map each set
of attributes $s$ to a sequence of hashes $M_1(s)\ldots M_k(s)$.  Now
the problem has been reduced to pairing users that have the same hash
sequence, which is similar to the original problem. Here $k$ can be varied appropriately to increase or decrease the probability of match for a pair of
slightly different attribute sets.

\ans{missing}
The idea here is very similar to hashing. Consider a very simple hash
function $F(x) = x \bmod{(n+1)}$. We can build a bit-vector of length
$n+1$ that is initialized to $0$ and for every element in $A$, we set
bit $F\big(A[i]\big)$ to 1.  Since there are only $n$ elements in the array,
there has to be at least one bit in the vector that is not set. That
would give us the number that is not there in the array.

An even simpler approach is to find the max (or min) element in the array
and return one more (less) than that element. This approach will not work if the
extremal elements are the largest (smallest) values in the set
that the entries are drawn from.

\begin{comment}

Suppose you have a scheme for finding patterns in stock trends. You may want to see how
good it is with respect to the best possible scheme. One way to formalize this is to
consider an array $A$ of $n$ integers representing the price of a share of a company's stock
over $n$ days. Given $A$, how would you efficiently determine the maximum amount of money
you could make by buying and selling a share over those $n$ days?

The brute force algorithm is quite simple: Buy the share at or before the beginning of any
rise, and sell at or before the beginning of any drop. Assuming you have to start by
buying a share the first day, you would have an outer loop that ranges from day $0$
to $n - 1$ and for each day, determine if the price is
rising (in which case you buy or hold) or
falling (in which case you sell or avoid buying). The way to determine this is simply to
look at the next day. Here is a sample Python implementation:

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Python]
def MaxDiff(arr):
    holding = False
    account = 0
    for i in range(len(arr) - 1):
        if arr[i + 1] > arr[i] and holding == False:
            # buy!
            holding = True
            account = account - arr[i]
        elif arr[i + 1] < arr[i] and holding == True:
            # sell!
            holding = False
            account = account + arr[i]

    # if we end up holding the stock, sell it at the end for the final profit
    if holding == True:
        account = account + arr[i + 1]
        holding = False

    return account
\end{lstlisting}

This yields:

{\footnotesize
\begin{verbatim}
ar1 = [5, 7, 9, 2, 2, 1, 3, 7, 12, 15, 12, 16, 8, 8, 2, 19] # max profit should be 39
MaxDiff(ar1) = 39

ar2 = [8, 7, 6, 5, 4, 3, 2, 1] # max profit should be 0
MaxDiff(ar2) = 0

ar3 = [1, 2, 3, 4, 5, 6, 7, 8] # max profit should be 7
MaxDiff(ar3) = 7
\end{verbatim}
}

Is there any better way to do this? Already we are using an $O(n)$ algorithm as each step
compares only the current and following days which is constant work. With it not being a
sorted (or sortable) list, I can't see a way to avoid hitting every element once; there
may be ways to do less work on each step, but I don't see a way to do it asymptotically
faster.
\end{comment}

\ans{max-difference}
Since the energy is only related to the height of the robot, we can
ignore $x$ and $y$ co-ordinates. Let's say that the points where the
robot goes in successive order have heights $h_1,\ldots,h_n$.
Let's assume that the battery capacity is such that with full battery, the robot can climb up $B$ meters. Then the robot will  run
out of battery iff there exist integers $i$ and $j$ such that $i <
j$ and $h_j - h_i > B$. In other words, in order to go from point $i$
to point $j$, the robot needs to climb more than $B$ points.  So, we
would like to pick $B$ such that for any $i < j$, we have $B \ge h_j - h_i$.

If we did not have the constraint that $i < j$, then we could just
compute $B$ as $\max(h) - \min(h)$ but this may be an overestimate:
consider the case when the robot is just going downwards.

We can compute the minimum $B$ in $O(n)$ time
if we keep the running min as we do a sweep. In code:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
double BatteryCapacity(vector<double> h) {
  if (h.size() < 2) {
    return 0;
  }
  double min = h[0];
  double result = 0;
  for (int i = 1; i < h.size(); ++i) {
    if (h[i] - min > result) {
      result = h[i] - min;
    }
    if (min > h[i]) {
      min = h[i];
    }
  }
  return result;
}
\end{lstlisting}

\ans{majority}
Let's first consider just the strict majority case.
This problem has an elegant solution when you make the following
observation: if you take any two distinct elements from the stream
and throw them away, the majority element remains the majority of the remaining
elements (we assumed there was a majority element to begin with). The reasoning goes
as follows: let's say the majority element occurred $m$ times out
of $n$ elements in the stream such that $m/n > 1/2$.  The two distinct
elements that we choose to throw can have at most one of the majority
elements. Hence after discarding them, the ratio of the previously
majority element could be either $m/(n-2)$ or $(m -1) /(n-2)$.  It
is easy to verify that if $m/n > 1/2$, then $m/(n-2) > (m -1) /(n-2)  > 1/2$.

Now, as we read the stream from beginning to the end, as soon as we
encounter more than one distinct element, we can discard one instance
of each element and what we are left with in the end must be the
majority element.
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
string FindMajority(stream* s) {
  string candidate, next_word;
  int count = 0;
  while (s->GetNext(&next_word)) {
    if (count == 0) {
      candidate = next_word;
      count = 1;
    } else if (candidate == next_word) {
      count++;
    } else {
      count--;
    }
  }
  return candidate;
}
\end{lstlisting}

The code above assumes there is a majority word in the
stream; if no word has a strict majority, it still returns
a string but there are no meaningful guarantees on what that string would be.

\ans{majority2}
This is essentially a generalization of Problem~\ref{majority}. Here
instead of discarding two distinct words, we discard $k$ distinct words
at any given time and we are guaranteed that all the words that occurred
more than $1/k$ times the length of the stream before discarding continue
to have more than $1/k$ fraction of copies.  For implementing this
strategy, we need to keep a hash table of current $k$ candidates. Here
is an example code:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
void FindFrequentItems(stream* s, hash_map<string, int>* word_set, int k) {
  word_set->clear();
  string word;
  while(s->GetNextWord(&word)) {
    hash_map<string, int>::iterator i = word_set->find(word);
    if (i == word_set->end()) {
      if (word_set->size() == k) {
        // Hash table is full, decrement all counts, which
        // is equivalent to discarding k distinct words.
        for (hash_map<string, int>::iterator j = word_set->begin();
             j !=  word_set->end();
             ++j) {
          --(j->second);
          if (j->second ==0){
            word_set->erase(j);
          }
        }
      } else {
        (*word_set)[word] = 1;
      }
    } else {
      i->second++;
    }
  }
}
\end{lstlisting}
It may seem the above code is taking $O(n.k)$ time since
the inner loop may take $k$ steps (decrementing count for all $k$
entries) and the loop goes on for $n$ times. However if you note that
each word in the stream can only be erased once, then the total time spent erasing everything is $O(n)$ and the rest of the steps inside the loop run in constant time.

The above code provides us with a $k -1$ size set of words
that is a superset of the words that occur more than $n/k$ times.
In order to get the exact set, we need to make another pass over the
stream and count the number of times each word in the hash table
actually occurs so that we  keep only the words which occur more than $n/k$ times.

\ans{bst}
A recursive solution is natural:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
Node* SearchBST(Node* root, int key) {
  if (root == NULL) {
    return NULL;
  } else if (root->key == key) {
    return root;
  } else if (root->key < key) {
    return SearchBST(root->left, key);
  } else {
    return SearchBST(root->right, key);
  }
}
\end{lstlisting}

Recursion adds the overhead of function calls.
The code above is not literally tail recursive, which means
that an optimizing compiler is unlikely to remove the recursive calls;
however there still is a straightforward iterative solution:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
Node* SearchBST(Node* root, int key) {
 while(root != NULL) {
   if (root->key == key) {
     return root;
   } else if (root->key < key) {
     root = root->left;
   } else {
     root = root->right;
   }
 return NULL;
}
\end{lstlisting}

\ans{bst-next}
This is similar to Problem~\ref{bst} but you just have to
continue your binary search till the end even if you find the element
that you were looking for and also keep track of the last element
that met the criteria.
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
Node* SearchBST(Node* root, int key) {
  Node* result = NULL;
  while(root != NULL) {
    if (root->key > key) {
      result = root;
      root = root->left;
    } else {
      root = root->right;
    }
  }
  return result;
}
\end{lstlisting}

\ans{k-smallest-int-2-array}
This problem requires some creative use of the binary search
idea. Let's say that the two arrays are $A1$ and $A2$ and say that $l$ of the $k$ smallest elements of the union
come from the first array and $l-k$ elements come from the second array.
If this were indeed true, then we would see that $A1[l-1] \le A2[k-l]$
and $A2[l-k]-1 \le A1[l]$ (barring some corner cases where we reach
the end of the array).

The other interesting observation we can make is that if $A1[l-1] >
A2[k-l]$, then we should use at least one more element from the second
array in the $k$ smallest elements. Similarly, if $A2[l-k-1] > A1[l]$,
then we should use at least one more element from the first
array. Using these two inequalities, we can essentially do a binary
search on $l$.  Note that this problem gives you plenty of corner
cases to worry about. In code:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
int FindOrderStat(const vector<int>& a1,
		  const vector<int>& a2,
		  unsigned int k) {
  // Check the validity of input.
  assert(a1.size() + a2.size() >= k);
  assert(k > 0);
  // Find an index begin <= l < end such that a1[0]..a1[l-1]
  // and a2[0]..a2[k-l-1] are the smallest k numbers.
  unsigned int begin = max(0, k - a2.size());
  unsigned int end = min(a1.size(), k);
  while(begin  < end) {
    unsigned l = begin + (end - begin)/2;
    // Can we include a1[l] in the k smallest numbers?
    if((l < a1.size()) && (k-l > 0) && (a1[l] < a2[k-l-1])) {
      begin = l + 1;
    } else if ((l > 0) && (k-l < a2.size()) && (a1[l-1] > a2[k-l])) {
      // This is the case where we can discard a[l-1]
      // from the set of k smallest numbers.
      end = l;
    } else {
      // We found our answer since both the inequalities were false.
      begin = l;
      break;
    }
  }
  if (begin == 0) {
    return a2[k - 1];
  } else if (begin == k) {
    return a1[k-1];
  } else {
    return max(a1[begin -1], a2[k - begin -1]);
  }
}
\end{lstlisting}

\ans{intersecting-lines}
Consider two lines  $y = a_i + b_ix$ and
$y = a_j + b_j x$  such that $a_i > a_j$.
The $i$-th line intersects the line $x = 0$ at $(0, a_i)$ and the
$j$-th line intersects the line $x = 0$ at $(0,a_j)$. Similarly,
these lines intersect $x = 1$ at $(1, a_i + b_i)$ and $(1,
a_j + b_j)$. Lines $i$ and $j$ intersect iff
\ifthenelse{\boolean{createspace}}{
\[
\big((a_i >
a_j) \& (a_i + b_i < a_j + b_j)\big) | \big((a_i < a_j) \& (a_i + b_i > a_j +
b_j)\big) .  \]
} {
\begin{eqnarray*}
\lefteqn{\big((a_i > a_j) \& (a_i + b_i < a_j + b_j)\big)}  &  &  \\
& & | \; \big((a_i < a_j) \& (a_i + b_i > a_j + b_j)\big) .
\end{eqnarray*}
}
In other words, for the lines to intersect, if $a_i < a_j$,
then it must be the case that  $(a_i + b_i < a_j + b_j)$ or vice versa
(ignoring the trivial
case where they intersect on one of the boundaries).

Hence if we sort the pairs $(a_i, b_i)$ by $a_i$ and test that for
successive pairs $(a_i, b_i)$ and $(a_j, b_j)$ if $a_i + b_i < a_j +
b_j$, we know that they do not intersect. If we do find a
violation of this inequality, then we have found one of the
intersecting pairs.

Sorting takes $O(n \log n)$ time and comparing successive pairs
takes $O(n)$ time. Hence this can be done in $O(n \log n)$ time.

\ans{overlapping-lines}
One way to solve this is to sort the intervals by their lower boundary
and see if their upper boundary is also sorted in the same order. If
not, we are sure to find some pair of indices $l,m$ where $a_l \le
a_m$ and $b_l \ge b_m$. This would be the pair we are looking for. If
the upper boundaries are also sorted, then we are guaranteed that no
interval is completely contained in another interval. Since this
involves sorting followed by a linear scan, we can get this done in
$O(n \log n)$ time.

\ans{2d-render}
The key idea here is to sort the endpoints of the lines and do a
sweep from left to right. As we do the sweep, we maintain a list of
lines that intersect the current position as well as the highest line
and its color.  In order to quickly lookup the highest line among
the set of intersecting lines, we can keep an ordered binary tree data-structure and to lookup the lines by the endpoint quickly, we can
maintain a hash table.

\ans{completion-search}
Define $F(\sigma)$ to be $\sum_{i=1}^{n}
\min(s_i,\sigma)$.  We are looking for a value of $\sigma$ such that $F(\sigma) =
S'$. Clearly, $F$ monotonically increases with $\sigma$. Also, since
$0 \le S' \le S$, the value of $\sigma$ is going to be between $0$ and
$\max(s_i)$. Hence we can perform a binary search like operation for
finding the correct value of $\sigma$ between 0 and $\max(s_i)$.

Assume that the $s_1,\cdots,s_n$ are already sorted, i.e., for all $i$, $s_i
\le s_{i+1}$.  Compute the running sum $z_k = \sum_{i=1}^{k}{s_i}$.

Now, suppose $s_k \le \sigma \le s_{k+1}$. Consequently, \[F(\sigma) = (n - k) \cdot \sigma + z_k .\]

Using the above expression, we can search for the value of $k$ such that
$F(s_k) \le S' \le F(s_{k+1})$ by performing binary search for $k$
(since the runtime of this solution is already $\Theta(n \log n)$, we
can do a linear search as well for simplicity). Once we have found
the right value of $k$, we can compute the value of $y$ by simply
solving the equation for $F(\sigma)$ above.

The most expensive operation for this entire solution is sorting the $s_i$s,
hence the runtime is $O(n \log n)$. However if we are given the $s_i$s  in
advance and we are allowed preprocessing, then for each value of $S'$,
the search would just take $O(\log n)$ time.

\ans{matrix-search}
A solution to this problem is discussed in the context of finding Hardy-Ramanujan numbers (Problem~\ref{Hardy-Ramanujan}).

\ans{simple-polygon}
Given two line segments in a two-dimensional plane, we can test for
intersection easily in constant time. Given $n$ line segments of a
polygon, we can find if any of the segments intersect in $O(n^2)$ time
by simply testing each pair. However doing this in $O(n \log n)$ time
requires a fairly complex algorithm.

Consider two line segments and the two farthest vertical lines that each intersect
with both the line segments (one vertical line is the leftmost
vertical line that still intersects with both lines and the other one
is the rightmost). The two line segments would intersect iff their vertical ordering changes between the two vertical lines.

The key idea is to use a sweep line, a vertical line that moves from
left to right through each endpoint.  We order the polygon vertices
(endpoints of line segments) from left to right first by increasing the
$x$ co-ordinate, then by increasing the $y$ co-ordinate. Now, imagine a
vertical line moving from left to right through these $2n$ endpoints.

For each position of this vertical line, we keep an ordered list of
intersecting line segments. The list is sorted by the $y$ co-ordinate of
the first endpoint of the line segment.  As we reach the starting points
of the new line segments, we insert them by doing a binary search for
them. As we reach the end of a line segment, we remove it from the
list. The sorted list can be maintained using a balanced BST.

When any line segment ends, we test if its vertical ordering changed
compared to the other lines in the list (which can be done in constant
time by just comparing the nearest two lines). The lines intersect iff the ordering changed for some line segment.

\chapter{ Sorting}

\ans{good-sort}
In general, Quicksort is considered one of the most efficient sorting
algorithms since it has a runtime of $\Theta(n\log_2 n)$ and it sorts
in-place (sorted data is not copied to some other buffer).
So, for a large set of random integers, Quicksort would be our choice.

Quicksort has to be implemented carefully---for example, in a na\"{i}ve
implementation, an array with many duplicate
elements leads to quadratic runtimes (and a high likelihood of stack space
being exhausted because of the number of recursive calls)---this can
be managed by putting all keys equal to the pivot in the correct place.
Similarly, it is important to call the smaller subproblem first---this,
in conjunction with tail recursion ensures that the stack depth is $O(\log_2 n)$.

However there are cases where other solutions are more preferable:
\begin{itemize}
\itemsep 1pt
\item {Small set}---for a very small set (for example, 3-4 integers),
  a simple implementation such as insertion sort is easier to code, and runs faster.
\item {Almost sorted array}---if every element is known to be at most $k$ places from its
final location, a min-heap can be used to get an $O(n\log_2 k)$ algorithm (Problem~\ref{app-sort}); alternatives
are bubble sort and insertion sort.
\item {Numbers from a small range, small number of distinct keys}---counting sort, which records for
each element, the number of elements less than it. This count can be kept
in an array (if the largest number is comparable in value to the size of the set being sorted) or a BST, where the keys are the numbers and the values are their
frequencies.
\item {Many duplicates}---we can add the keys to a BST, with linked lists for elements
which have the same key; the sorted result can be derived from an in-order walk of the BST
\item {Stability is required}---most useful sorting algorithms are not stable.
Mergesort, carefully implemented, can be made stable; another solution is to add
the index as an integer rank to the keys to break ties.
\end{itemize}

\ans{big-sort}
When sorting data that cannot fit into the RAM of a single machine, we have to partition the data into
smaller blocks that would fit in the memory, sort each block individually, and then combine the blocks. If a cluster of machines is available,
the blocks can be sorted in parallel or they can be read in sequence
on a single machine and then stored on the disk.

There are two popular approaches for doing this. If we know the rough
distribution of the data in advance (e.g., it is distributed uniformly),
it can be partitioned into contiguous subranges of approximately equal size
 in the first pass. This has the advantage that
once the individual blocks are sorted, we can combine them
just by concatenation.

Another slightly more expensive approach that does not require any
knowledge of distribution is to read the input data in sequence till the memory is full, sort it, write it, and then read the next block till we are done with the file. This requires us to merge the sorted blocks in the end like Mergesort. Here, since we could be potentially merging a large number of sorted files, using a min-heap is
helpful. Essentially, we keep the smallest unread entry from each file
in the heap, then we extract the min element from the heap, replace
it with the next entry from the same file, and write out the min value
to the output file.

The Unix \texttt{sort} program is
very robust; it makes use of the disk when needed and can combine
a set of files into a single sorted file.

\ans{max12}
First, we consider the problem of finding the best player.
Each game eliminates one player and there are 128 players; so, 127 matches are necessary and also sufficient.

To find the second best, we note that the only candidates are
the players who are beaten by the player who is eventually determined
to be the best---everyone else lost to someone who is not the best.

To find the best player, the order in which we organize the matches
is inconsequential---we just pick pairs from the set of
candidates and whoever loses is removed from the pool of candidates.
However if we proceed in an arbitrary order, we might start with the
best player, who defeats 127 other players and  then the players who
lost need to
play 126 matches amongst themselves to find the second best.

We can do much better by organizing the matches as a binary tree---we pair
off players arbitrarily who play 64 matches. After these matches, we are
left with 64 candidates; we pair them off again arbitrarily and they play 32 matches.
Proceeding in this fashion, we organize the 127 matches needed to find the
best player and the winner would have played only 7 matches. Therefore we can find the second
best player by organizing 6 matches between the 7 players who lost to the best player,
for a total of 134 matches.

\ans{min-max}
Split the numbers into pairs of two and then group the higher values of
the pairs into one set and the lower values into another set.
 Find the min of the lower group and the max of
the higher group.

\ans{top3}
Let's start with five time-trials with no cyclist being in more than one
of these five initial time-trials. Let the rankings be
$A1,A2,A3,A4,A5$, $B1,B2,B3,B4,B5$,
$C1,C2,C3,C4,C5$, $D1,D2,D3,D4,D5$, and
$E1,E2,E3,E4,E5$, where the first cyclist is the fastest.
Note that we can eliminate $A4,A5,B4,B5,C4,C5,D4,D5,E4,E5$ at this stage.

Now, we race the winners from each of
the initial time-trials. Without loss of generality, assume the outcome is $A1,B1,C1,D1,E1$.
At this point, we can eliminate $D1$ and $E1$ as well as $D2,D3$ and $E2,E3$.
Furthermore, since $C1$ was third, $C2$ and $C3$ cannot be in the top three;
Similarly, $B3$ cannot be a contender.

We need to find the best and the second best from $A2,A3,B1,B2,C1$, which we can determine with
one more time-trial, for a total of seven time-trials.

% A lower bound of 6 races is easy to prove: if the first 5 races are not disjoint,
% someone has not raced yet, and he may be the top cyclist. Conversely, if
% the first 5 races are disjoint, we need one more races to compare the winners.
% We can tighten the lower bound to 7 by supposing that say $A1$ is not picked

Note that we need time-trials to determine the overall winner, and the sequence of time-trials
to determine the winner is essentially unique---if some cyclist did not participate in the first
five time-trials, he would have to participate in the sixth one. But then one of the winners
of the first five time-trials would not participate in the sixth time-trial and he might
be the overall winner.  The first six time-trials do not determine the second and the third fastest
cyclists, hence a seventh race is needed.

\ans{distance-sort}
Whenever the swap operation for the objects being sorted is expensive, one of the best things to
do is indirect sort, i.e., sort references to the objects first and
then apply the permutation that was applied to the references in the
end.

In the case of statues, we can assign increasing indices to the
statues from left to right and then sort the pairs of statue height
and index. The indices in the sorted pairs would give us the
permutation to apply.  While applying permutation, we would want to
perform it in a way that we move each statue the minimum possible
distance. We can achieve this if each statue is moved exactly to its
correct destination exactly once (no intermediate swaps).

\ans{privacy}
The simplest way of doing this would be to define a lexicographic
ordering over the rows (where we ignore the contents of deleted
columns) and sort the rows.  Once the rows are sorted, we can count
the number of duplicate rows for each unique row easily in a linear
pass. In case it is expensive to swap the rows (since each row contains
large amounts of data), it might be more efficient to hash the contents
of the row and sort the hash values instead.

\ans{var-sort}
Almost all sorting algorithms rely on swapping records. However this
becomes complicated when the record size varies. One way of
dealing with this problem is to allocate for the maximum possible
size for each record---this can be very wasteful if there is a large
variation in the sizes.

Here also indirect sort can be helpful---keep the records in a compact form in
the memory and build another array of pointers to the records.
Then we just sort the pointers using
the compare function on the de-referenced pointers
and finally write the data by de-referencing the sorted pointers.

\ans{dup-sort}
An efficient way of eliminating duplicates from any set of records,
where a ``less-than'' operation can be defined, is to sort the records
and then eliminate the duplicates in a single pass over the data.

Sorting can be done in $O(n\log n)$ time;  the subsequent elimination of
duplicates takes $O(n)$ time. If the elimination of duplicates is
done in-place, it would be more efficient than writing the unique set
in a separate array since we would achieve better cache performance.
Here is the code that does in-place duplicate removal:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
size_t EliminateDuplicates(int* array, size_t length) {
  size_t j = 1;
  for (size_t i = 1; i < length; i++) {
    if (array[i] != array[j-1]) {
      array[j] = array[i];
      j++;
    }
  }
  return j;
}
\end{lstlisting}

Another efficient way is to use hash tables where we
store each record into a hash table as the key with no value and
then write out all the keys in the hash table. Since hash table
inserts can be done in $O(1)$ time and iterating over all the keys also takes
only $(n)$ time, this solution scales much better than the sorting
approach. However, in practice, for small size of inputs, the sorting
approach might work faster since it can be done in-place.

\ans{merge-sorted-arrays}
While merging $k$ sorted arrays, we need to repeatedly pick the smallest
element amongst the smallest remaining records from each array. A
min-heap is ideal for maintaining a set of records when we
repeatedly insert and query for the smallest record (both extract-min and insert
would take $O(\log k)$ time). Hence we can do the merge in $O(n\log k)$ time,
where $n$ is the total number of records in the input. Here is the
code for this:

%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
bool Greater(const pair<int, int>& a,
	     const pair<int, int>& b) {
  if (a.first > b.first) {
    return true;
  } else if (a.first == b.first && a.second > b.second) {
    return true;
  } else {
    return false;
  }
}

void MergeSortedVectors(
    const vector<vector<int> >& sorted_input,
    vector<int>* output) {
  // The first number is the smallest number remaining and
  // the second number represents array from which it was taken.
  vector<pair<int, int> > min_heap;
  // We keep an index of the numbers read from each array.
  vector<int> current_read_index(sorted_input.size());
  for (int i = 0; i < sorted_input.size(); i++) {
    if (sorted_input[i].size() > 0) {
      min_heap.push_back(make_pair(sorted_input[i][0], i));
      current_read_index[i] = 1;
    }
  }

  make_heap(min_heap.begin(), min_heap.end(), Greater);

  while (min_heap.size() > 0) {
    pair<int, int> min = min_heap[0];
    pop_heap(min_heap.begin(), min_heap.end(), Greater);
    min_heap.pop_back();
    output->push_back(min.first);
    if (current_read_index[min.second] <
        sorted_input[min.second].size()) {
      // There are more inputs to be read. Read the next number
      // and insert it in the heap.
      min.first = sorted_input[min.second][current_read_index[min.second]];
      current_read_index[min.second]++;
      min_heap.push_back(min);
      push_heap(min_heap.begin(), min_heap.end(), Greater);
    }
  }
}
\end{lstlisting}

% \ans{merge-sorted-arrays}

\begin{comment}
Ian's solution

Merge sorted arrays

Given $k$ sorted arrays of integers, return a sorted array containing the elements of the
sorted arrays, including any duplicates.

When combining multiple lists into an output, the ``merge'' step of merge sort comes
 to mind immediately---take items off the top of $k$ sorted sublists and combine them into a
single output. This requires $k$ comparisons for each item in $n$, so  overall we require $O(k \cdot n)$
operations. If $k$ is small, this is essentially linear in $n$.
Code for that process in Python:

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Python++]
def TryMerge(k, n, a, b):

    # construct a list of k arrays of n random elements
    # in the range of a to b
    lists = GetListOfSortedArrays(k, n, a, b)

    # pass them in to Merge and report the output
    return Merge(lists)

def GetListOfSortedArrays(k, n, a, b):
    lists = list()
    for i in range(k):
        l = []
        for j in range(n):
            l.append(random.randint(a, b))
        l.sort()
        lists.append(l)
    return lists

def Merge(lists):

    # maintain dictionary of k pointers to the next item in each array
    pointers = {}
    for k in range (len(lists)):
        pointers[k] = 0 # init all pointers to the start of their list

    output = []
    while len(pointers) > 0:
        # look at the top item in each list
        min = None
        p = None
        for k in pointers.keys():
            val = lists[k][pointers[k]]
            if min == None or val < min:
                min = val
                p = k

        # add the item to the output and increment the pointer
        output.append(min)
        pointers[p] = pointers[p] + 1

        # if the pointer is past the last item the list, remove
	# the pointer
        if pointers[p] >= len(lists[p]):
            del(pointers[p])

    return output
\end{lstlisting}

And some output:

\begin{verbatim}
TryMerge(5, 5, 1, 100)

Original: [[13, 63, 69, 72, 96], [2, 5, 7, 51, 92], [1, 12, 12, 33, 69],
	   [10, 12, 29, 44, 99], [8, 19, 19, 25, 80]]

Result:   [1, 2, 5, 7, 8, 10, 12, 12, 12, 13, 19, 19, 25, 29, 33, 44, 51,
		63, 69, 69, 72, 80, 92, 96, 99]
\end{verbatim}

If, on the other hand, $k$ is large (for example, approaching the size of $n$) then the operation
tends to $O(n^2)$ or greater, which is bad.

Instead, assuming there is enough space to store an additional copy of each element, it is
possible that inserting all the items into a heap and then pulling them off one at a time
could be quicker. Each heap insert requires $O(\log n)$ time, so building the entire heap
should require $O(n lg n)$ time. However there is a method called ``bubble down'' that will
actually create a heap from an array in roughly linear-time. Doing that would then allow
us to get the list in $O(n)$ time even for large $k$.

Here is the Python code approximating that solution:

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
def TryHeap(k, n, a, b):

    # construct a list of k arrays of n random elements
    # in the range of a to b
    lists = GetListOfSortedArrays(k, n, a, b)

    # return sorted version using a heap
    return HeapSort(lists)

def HeapSort(lists):

    # put the items into contiguous storage
    input = []
    for l in lists:
        input.extend(l)
    count = len(input)

    # heapify (using python's built in library, which does bubble down
    # heapification in near linear-time)
    heapq.heapify(input)

    # pull min value into output until heap is empty
    output = []
    for i in range(count):
        output.append(heapq.heappop(input))
    return output
\end{lstlisting}

This also correctly sorts the input lists.

Of these two approaches, which one performs better on the same input?
We can add timing decorators to the function like so:

\begin{verbatim}
import time
def print_timing(func):
    def wrapper(*arg):
        t1 = time.time()
        res = func(*arg)
        t2 = time.time()
        editor.AddText("\n" + func.func_name
		+ " took " + str((t2-t1)*1000.0) + " ms\n")
        return res
    return wrapper
\end{verbatim}

We then add a decorator to each function:

\begin{verbatim}
@print_timing
def TryHeap(k, n, a, b):
    ...
\end{verbatim}

The timings thus obtained tell us about the function's performance in
different situations. As we might expect, with a small $k$,
the merge version beats the heap version:

\begin{verbatim}
>> TryMerge(2, 1000000, 1, 10000)
TryMerge took 12014.9998665 ms

>> TryHeap(2, 1000000, 1, 10000)
TryHeap took 13969.0001011 ms
However, with a larger k, the heap version is faster (about five times as fast in one implementation):

>> TryMerge(100, 3000, 1, 10000)
TryMerge took 10780.9998989 ms

>> TryHeap(100, 3000, 1, 10000)
TryHeap took 2062.99996376 ms
\end{verbatim}

Doubling $k$ roughly quadruples the time (which is consistent with
the assertion that the performance is $O(n^2)$ as $k$ approaches $n$). By
comparison, doubling $k$ in the heap version is (expectedly) a linear doubling:

\begin{verbatim}
>> TryMerge(200, 3000, 1, 10000)
TryMerge took 38062.0000362 ms

>> TryHeap(200, 3000, 1, 10000)
TryHeap took 4266.00003242 ms
\end{verbatim}

\end{comment}

\ans{app-sort}
The easiest way of looking at this problem is that we need to store
the numbers in memory till all the numbers smaller than this number have
arrived. Once those numbers have arrived and have been written to the
output file, we can go ahead and write this number. Since we do not
know how the numbers are shuffled, it is hard to tell when all the numbers
smaller than a given number have arrived and have been written to
the output. However since we are told that no number is off by more
than one thousand positions from its correctly sorted position, if more than
a thousand numbers greater than a given number have arrived and all the numbers
smaller than the given number that arrived have been written, we can
be sure that there are no more other smaller numbers that are going to
arrive. Hence it is safe to write the given numbers.

This essentially gives us the strategy to always keep 1001 numbers in
a min-heap. As soon as we read a new number, we insert the new
number and then extract the min
from the heap and write the output.

\begin{comment}
Ian's solution

There is a stream of numbers arriving as an input such that each number is at most 1000
positions away from its correctly sorted position. Write an algorithm that outputs
correctly sorted numbers but uses only $O(1)$ storage.

The problem is essentially that of buffering a constant number of input items, $m$ (in this
case, $m = 1000$). A good data-structure to use for this (assuming ascending order) would be
a min-heap. If we use a heap, each insert would take $\log m$ time. Since $m$ is a constant, this is really a constant time operation.

Initially we would need to buffer $(m \log m)$ records. For each additional item that comes in
via the stream, we first insert it into the heap ($O(lg m)$.
Then we pop the min item off the heap. All the heap operations would be constant time with respect to $n$ since we are treating $m$ as a constant.

Here is a Python implementation. First, we have to simulate an ``almost sorted'' stream which we can do with this Python function:

\begin{verbatim}
import random
import heapq
import math

# Create an almost-sorted array of numbers
def GetInputStream(n, m):
    ordered = range(0, n)
    for i in range(0, n):
        ordered[i] = random.randint(0, n)
    list.sort(ordered)
    permuted = range(0, n)
    for i in range(0, n):
        permuted[i] = None

    # for each element, put it into a spot that's +/- m away
    # from its original spot, checking first to see that
    # that spot is available
    for x in range(n):
        moved = False
        # first, see if I am forced into a slot by there being
	# an opening that is m lower than me
        if x >= m and permuted[(x - m)] == None:
            permuted[(x - m)] = ordered[x]
            moved = True
        else:
            while moved == False:
                newpos = random.randint(max(x - m, 0), min(x + m, n - 1))
                if permuted[newpos] == None:
                    permuted[newpos] = ordered[x]
                    moved = True

    return permuted
\end{verbatim}

This produces an input stream where every element is at most $m$ positions away from its
eventual sorted order. The original source material is a random list of numbers that has
been put in ascending order. Each element is then moved to a randomly determined
unoccupied position that is +/- $m$ slots away (taking care no gaps are left).

Then, to sort the items, each is inserted into a min-heap, and when the heap size is
greater than m, one item is popped off (i.e., the min item is returned and removed from the
heap and the heap is re-heapified) for every item that is pushed on.

\begin{verbatim}
def AlmostSorted(n, m):
    # simulate a stream of random numbers by creating an almost-sorted
    # array of n numbers, and iterating over them
    input = GetInputStream(n, m)

    # prepare an output array to store the output in; if we
    # were really streaming, this would not exist
    output = range(0, n)
    for i in range(0, n):
        output[i] = None
    counter = 0

    # put each item into the heap, and if the size of the
    # heap is over m, output the smallest item
    heap = []
    heapq.heapify(heap)
    heapsize = 0
    for i in range(n):
        heapq.heappush(heap, input[i])
        heapsize = heapsize + 1
        if heapsize > m:
            output[counter] = heapq.heappop(heap)
            counter = counter + 1

    # at the end of the stream, output the remaining items from the heap
    while counter < len(output):
            output[counter] = heapq.heappop(heap)
            counter = counter + 1

    return output
\end{verbatim}

This produces an output in sorted order, in constant time for each item regardless of the
size of $n$. (Of course, for this Python implementation, the entire program must execute,
making the operation time linear in $n$).

\end{comment}

\ans{running-average}
While it takes $O(k)$ time to compute the average of a window of size
$k$, the successive averages for the sliding window can be computed
inexpensively
by maintaining the sum over the sliding window. When the window is
slid by one position, the new sum can be computed like this:
$sum_{i+1} = sum_i + x[i+k+1] - x[i]$.  Hence the entire running
average can be computed in $O(n)$ time.

Computing the running median is a bit more involved but the same idea is applicable there as well.
When we slide the window by one position, we delete the first element
from the list and insert the next element.
Therefore we need to maintain a set
in a way that allows us to find the median easily in the presence of
inserts and deletes.
This can be achieved with a balanced BST (an AVL tree or a red-black tree could do the job).
Both insert and delete are $O(\log k)$ operations.
Finding the median after an update amounts to looking for the successor or
predecessor
of the existing median depending on whether the update involved an element
that was larger or smaller than the current median.
Therefore we can compute the running median in $(n\log k)$ time for the entire series.

Alternately, we could just use an order-statistic tree which is simply a balanced BST with
some additional information stored at each node.  Specifically, in an order-statistic
tree, each node records the number of nodes in the subtree stored at that node.
Inserts and deletes can be done in $O(\log n)$ time and retrieving
an element with a given rank (which covers the median case)
can also be implemented in $O(\log n)$ time.

\ans{event-simulation}
Event driven simulation is a classic problem and is used in a number
of simulation applications including digital circuits. The main idea
here is that at any given point, we know all the future events that are
going to happen as a direct result of events that have happened so
far. Until the first event in these set of events happens, nothing
else is going to happen. Hence we can advance time to this event
without doing any new work.

In practice, this essentially amounts to maintaining a queue of events
that are going to happen as a direct result of past events, find the
event with the smallest time in this set, delete it from the set,
compute the events that this event would trigger, and then insert them
in the event queue. For this application again, a min-heap works
most efficiently.

\chapter{ Meta-algorithms}\normalsize

\ans{longest-nondecreasing}
Let $s_i$ be the length of the longest nondecreasing subsequence of $A$ that ends
at $A[i]$ (specifically, $A[i]$ is included in this subsequence). Then
we can write the recurrence
\[
s_i = \max\Big( \max_{j: A[j] \leq A[i], j < i} (s_j +1) , 1 \Big) .
\]

Using this strategy, fill up a table for $s_i$. If we want the sequence as well, for
each $i$, in addition to storing the length of the sequence, we can
store the index of the last element of sequence that we extend to get
this sequence. Here is an implementation of the idea:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
void longestNondecreasingSequence(
    const vector<int>& input, vector<int>* output) {
  assert(output != NULL);
  output->clear();
  if (input.size() == 0) {
    return;
  }
  vector<int> longestSequenceLength(input.size(), 1);
  vector<int> previous_index(input.size(), -1);
  longestSequenceLength[0] = 1;
  int max_length = 1;
  int longest_sequence_end = 0;
  for (int i = 1; i < input.size(); i++) {
    int length = 1;
    int prev_index = -1;
    for (int j = 0; j < i; j++) {
      if (input[j] <= input[i] &&
          longestSequenceLength[j] + 1 > length) {
	length = longestSequenceLength[j] + 1;
	prev_index = j;
      }
    }
    longestSequenceLength[i] = length;
    previous_index[i] = prev_index;
    if (max_length < length) {
      max_length = length;
      longest_sequence_end = i;
    }
  }
  assert(output != NULL);
  output->clear();
  // Build the reverse of the longest sequence by going backwards from the end.
  while(longest_sequence_end >= 0) {
    output->push_back(input[longest_sequence_end]);
    longest_sequence_end = previous_index[longest_sequence_end];
  }
  std::reverse(output->begin(), output->end());
}
\end{lstlisting}

\ans{frog-crossing}
Let $P[x]$ be true iff there is a stone in the river at $x$ meters.

Let's define $F[x][y]$ to be a Boolean variable that is true iff it is
possible for the frog to reach $x$ meters from the shore with the last
jump being $y$ meters. We can say that $F[0][y]$ is true iff $y=0$. Also,
$F[x][y]$ can be true iff $P[x]$ is true (there is a stone there) and
that either $F[x-y][y]$,  $F[x-y][y+1]$, or $F[x-y][y-1]$ is true.
Using DP, we can compute the values of $F[n][y]$ for
all possible values of $y$. One interesting thing to note here is that
while jumping the first $n$ meters, the largest jump size could be at most
$\sqrt{2n}$. Hence we just need to worry about values of $y \le
\lceil \sqrt{2n} \rceil $.  This gives us a runtime of $O(n^{1.5})$. Here is a
possible implementation:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
bool isReachable(const vector<bool>& p) {
  if (p.size() == 0) {
    return true;
  }
  // Max attainable jump size.
  int m = sqrt(2 * p.size());
  vector<vector<bool> > f(p.size() + 1);
  // The first block can only be reached with jump of
  // size 1 and no block can be reached with jump of
  // size 0.
  for (int j = 0; j <= m; j++) {
    f[0].push_back(false);
  }
  for (int i = 1; i < p.size(); i++) {
    f[i].push_back(false);
  }
  if (p[0]) {
    f[0][1] = true;
  }
  for (int i = 1; i < p.size(); i++) {
    for (int j = 1; j <= m; j++) {
      f[i].push_back(false);
      if (p[i] && i - j >= 0) {
	if (f[i-j][j]) {
	  f[i][j] = true;
	} else if (j > 0 && f[i-j][j-1]) {
	  f[i][j] = true;
	} else if (j + 1 < m && f[i-j][j+1]) {
	  f[i][j] = true;
	}
      }
      if (f[i][j] == true && i + j + 1 > p.size() ) {
	//From this point the frog can cross the river in
	// a single jump.
	return true;
      }
    }
  }
  return false;
}

\end{lstlisting}

% \ans{max-submatrix}
% This is a fairly straightforward application of dynamic programming.
% Let $A_{m,n}$ be the input matrix. Let $A(a,b,c,d)$ represent the
% submatrix of $A$ that just contains rows $a$ through $c$, and columns
% $b$ through $d$. Let $Sum(X)$ be the sum of all the elements in matix
% $X$. Hence we want to find $\argmax_{a,b,c,d} Sum(A(x,b,c,d))$.

% Let's define $T(x,y)$ to be $max_{a,b}(Sum(A(a,b,x,y))$, i.e., the sum
% of the largest submatrix that ends in $A[x,y]$. We define $T[x,0]$ and
% $T[0,y]$ to be 0 for all values of $x$ and $y$. Then we

\ans{cutting-paper}
Since the machine we have can only cut a piece of paper into two
pieces either vertically or horizontally and all the final pieces have integer
length and width, this significantly limits the space we
have to explore. Let $V(x,y)$ be the maximum value we
can extract out of a paper of width $x$ and height $y$.
Let $U(x,y)$ be the price of a paper of dimension $x,y$ without
cutting (if we cannot sell it as is, the value is set to 0).

We assert that
\begin{eqnarray*}
\lefteqn{V(x,y)  =  \max} \\
 & & \Big(\max_{a \in [0,x]} \big(V(a,y) + V(x-a, y)\big),  \\
 & & \max_{b \in [0,y]} \big(V(x,b) + V(x, y-b)\big),  \\
 & & U(x,y)\Big).
\end{eqnarray*}

In other words, the value of the paper is the max of the cost of the two
vertically cut pieces or the two horizontally cut pieces or the paper
as is. Using this recurrence relationship, we can use DP to compute the values of $V$ of interest in $O(a\cdot b + n)$
time.
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
float computeMaxCost(int width, int length, vector<PaperPrice> prices) {
  vector<vector<float> > V;
  for (int i = 0; i <= width; i++) {
    V.push_back(vector<float>(length + 1, 0));
  }
  for (int i = 0; i < prices.size(); i++) {
    if (prices[i].length <= length && prices[i].width <= width) {
      if (V[prices[i].width][prices[i].length] < prices[i].price) {
	V[prices[i].width][prices[i].length] = prices[i].price;
      }
    }
  }

  for (int i = 1; i <= width; i++) {
    for (int j = 1; j <= length; j++) {
      for (int k = 1; k < i; k++) {
	if (V[i][j] < V[i-k][j] + V[k][j]) {
	  V[i][j] = V[i-k][j] + V[k][j];
	}
      }
      for (int k = 1; k < j; k++) {
	if (V[i][j] < V[i][k] + V[i][j-k]) {
	  V[i][j] = V[i][k] + V[i][j-k];
	}
      }
    }
  }

  float result = V[width][length];
  return result;
}
\end{lstlisting}

\ans{word-breaking}
This is a straightforward DP problem. If the input
string $S$ has length $n$, we build a table $T$ of length $n$ such that $T[k]$ is a
Boolean that tells
us if the substring $S(0,k)$ can be broken into a set of valid words.

We can build a hash table of all the valid words such that we can
determine if a string is a valid word or not in constant time.
Then $T[k] $ is true iff there exists a $j \in [0,k-1] $ such that
$T[j]$ is true and $S(j,k)$ is a valid word.

This will just tell us if we can break a given string into valid words
but would not give us the words themselves. With a little more
book-keeping, we can achieve that. Essentially, in table $T$ along with
the Boolean value, we can also store the beginning index of the last word
in the string.

If we want all possible decompositions, we can store all possible
values of $j$ that gives us a correct break with each
position. However the number of possible decompositions can be exponential
here. For example, consider the string ``itsitsitsits...''.

\ans{elec-college}
We need to determine if there is a subset of states whose Electoral College votes add up to $\frac{538}{2} = 269$. This is a version of the 0-1 knapsack problem described in Problem~\ref{01knapsack} and the  DP solution to that problem can be used.

\ans{red-blue-house}
Number the individual elections from $1$ to $446$.
Let $T(a,b)$ be the probability that exactly $b$ Republicans win out of elections $\{1,2,\ldots,a\}$.

Let $X_i$ be the event that a Republican wins the $i$-th race.
Then $T(a,b) = Pr(\sum_{i\leq a} X_i = b)$.
There are two ways in which the first $a$ random variables sum up to $b$:
the $a$-th random variable is 1 and the first $a-1$ variables sum up to $b-1$
or the $a$-th random variable is 0 and the first $a-1$ random variables sum up to $b$.
Since these events are exclusive, the probability $T(a,b)$ is the sum
of the probabilities of these two events.
To be precise, \[ T(a,b) = T(a-1,b-1) \cdot p_a + T(a-1,b) \cdot (1-p_a) . \]
The base cases for the recursion are $T(0,0) = 1$ and $T(0,b) = 0$, for $b > 0$.
Therefore $T$ can be computed using DP. Since both $a$ and $b$ take
values from $0$ to the number of races and computing $T(a,b)$ from
earlier values takes constant time, the complexity is quadratic in the number of races.

\ans{load-balancing}
% Let's say that you have $n$ users, with unique hashes  $h_1$ through $h_n$ and $m$ machines. Each user with hash  $h_i$ has  $B_i$  bytes to store.
%  You need to find numbers $K_1$ through $K_m$  such that all users with hashes between
% $k_j$ and $k_{j+1}$ get assigned to machine $j$.  How would find the numbers $K_1$ through $K_m$, such
% that the load on the most heavily loaded machine is minimized?
Let $L(a,b)$ be the maximum load on a server when users with hash $h_1$
through $h_a$ are assigned to servers $S_1$ through $S_b$ in an
optimal way so that the max load is minimized. We observe the following recurrence:
\[ L(a,b) = \min_{x\in\{1,\ldots,a\}}\Big(\max\big(L(x,b-1), \sum_{i =
  x+1}^a(B_i)\big)\Big) . \]

In other words, we find the right value of $x$ such that if we pack
the first $x$ users in $b-1$ servers and the remaining in the last
server, the max load on a given server is minimized.

Using this relationship, we can tabulate the values of $L$ till we get
 $L(n,m)$.  While computing $L(a,b)$ when the values of $L$ is
tabulated for all lower values of $a$ and $b$, we need to find the
right value of $x$ to minimize the load. As we increase $x$, $L(x, b-1)$ in
the above expression increases and the term $ \sum_{i =
  x+1}^a(B_i))$ decreases. Hence in order to find $x$ that
minimizes their max, we can do a binary search for $x$ which can be
done in $O(\log a)$ time. Therefore we can compute the load in
$O\big(m n\log(n)\big)$ time.

\ans{voltage-selection}
Let $V(g)$ be the voltage level assigned
to gate $g$. Let $I(g)$ be the
set of all gates that are inputs to $g$. Let $P(g)$  be the minimum possible power that can be achieved by
a legal assignment of voltages, wherein we choose a low voltage for gate  $g$.
Let $Q(g)$ be the  minimum possible power that can be achieved  when
$g$ is assigned a high voltage.
We can write the following recurrence relationship for $P$ and $Q$:
\begin{eqnarray*}
P(g) & = & 1 + \sum_{r \in I(g)}Q(r) \\
Q(g) & = & 2 + \sum_{r \in I(g)} \min \big( P(r), Q(r)\big).
\end{eqnarray*}

Using these equations, we can tabulate the values of $P$ and $Q$
for all gates and our answer is going to come from the maximum of the values of  $P$ and  $Q$ for the gate at the root of the tree.
Since we perform constant operations per gate,
the overall complexity is $O(G)$, where $G$ is the number of gates.

\ans{buffer-insertion}
We can formulate this DP in a manner similar to Solution~\ref{voltage-selection}.
For each node, we tabulate $k$ values. Let $N(u, l)$
be the minimum number of buffers needed for the subtree rooted at
node $u$, if the first buffer above this node appears $l$ or more hops away. The
recurrence relationship can be defined by
\ifthenelse{\boolean{createspace}}{
\[N(n, l) = \sum_{c \in I(n)}\min\Big( 1 + N(c, k), N\big(c, \min(l+1, k)\big)\Big) . \]
}
{
\begin{eqnarray*}
\lefteqn{[N(n, l) =} \\
& & \sum_{c \in I(n)}\min\Big( 1 + N(c, k), N\big(c, \min(l+1, k)\big)\Big) .
\end{eqnarray*}
}

We can tabulate the value of $N$ for all nodes from the leaf to the root for
all values of $l \le k$. Then $N(r,k)$, where $r$ is the root,
is the minimum number of buffers needed.
Since we perform $O(k)$ operations per gate,
the overall complexity is $O(G\cdot k)$, where $G$ is the number of gates.

\ans{triangulation}
Let's label the vertices of the polygon $1,\ldots,n$, starting from
an arbitrary vertex and walking clockwise.  Let $C(p_1,\ldots, p_k)$ be the cost
of triangulating the polygon formed by vertices $p_1$ through $p_k$. Let $L(a\cdot b)$ be the length of the straight line drawn from vertex
$a$ to vertex $b$.

 Now, we know that if the number of vertices in the polygon is three or
 less, the cost is zero.  Consider an edge $\langle p_i, p_{i+1}\rangle$. One of the
 triangles must contain this edge. The third vertex of the triangle is
 going to be another vertex, say $p_j$. Then the cost of triangulation
 is going to be the cost of triangle $\langle p_i, p_{i+1}, p_j\rangle$ and the
 cost of triangulation of the smaller polygons formed by removing this
 triangle (which may be 1 or 2 polygons depending upon whether $j = i
 + 2$ or not). For any pair of points $a$ and $b$, let $L(a,b)$ be the
 length of the line segment joining $a$ and $b$. Then we can write the
 following recurrence relationship:
\begin{eqnarray*}
\lefteqn{A(p_1, \ldots, p_k) = \min_{x: 3 \le x \le  k} \Big(} \\
& & A(p_3, \ldots, p_x)  \\
& & + A(p_x, \ldots,p_k,p_1) + L(p_1, p_2) + L(p_1,p_x) + L(p_2,p_x)\Big).
\end{eqnarray*}
If we tabulate the cost of triangulation of each polygon that is a
result of picking subsequent points on the original polygon, we
would need to do this for roughly $n^2$ polygons. If we have
already tabulated the value for all smaller polygons, it will take
us $O(n)$ time for doing so. Hence we can compute the minimum cost in
$O(n^3)$ time.

\begin{comment}
\ans{climbing}
Let $F(n)$ be the number of ways of climbing $n$ stairs through a
combination of 1 or 2 steps.  We can note that $F(1) = 1$ and $F(0) =
1$.
Now, all paths that lead us to cross $n$ steps either start with a
single step or a double step. In case of a single step, there are
$F(n-1)$ ways of completing the path. In case of a double step, there
are $F(n-2)$ ways of completing the path. Hence
\[ F(n) = F(n-1) + F(n-2) , \]
which leads to a simple dynamic programming algorithm that can compute
$F(n) $ in $O(n)$ time. An interesting thing to note
here is that $F(n)$ has the same recurrence relationship as Fibonacci numbers and $F(n)$ is actually $(n +1)$-th Fibonacci number.

\end{comment}

\ans{arith-expr}
We focus on the case where all the operands are nonnegative integers
and the only operations are $\cdot$ and $+$.

Represent the expression $v_0 \circ_0 v_1 \circ_1 \cdots \circ_{n-2} v_{n-1}$
by arrays $V = [v_0,\ldots,v_{n-1}]$ and $\circ_0,\ldots,\circ_{n-2}$.

Let $\mbox{Max}[i,j]$ denote
the maximum value achievable by some parenthesization for
the subexpression $v_i \circ_i v_i \circ_i \cdots \circ_{j} v_{j}$,
where $\mbox{Max}[i,j]$ is just $V[i]$.

The key to solving this problem is to recognize that if operation
$\circ_i$ is performed last, the subexpressions
$v_0 \circ_0 v_1 \circ_1 \cdots \circ_{i-2} v_{i-1}$ and
$v_{i+1} \circ_{i+1}  \cdots \circ_{n-2} v_{n-1}$ must be parenthesized
to be maximized individually.

In particular, the maximum value must be achieved
for some value of $i$ in $[0,n-2]$, so
\[
\mbox{Max}[0,n-1] = \max_{i \in [0,n-2]} \mbox{Max}[0,i] \circ_i \mbox{Max}[i+1,n-1] .
\]
The total number of recursive calls is $O(\binom{n}{2})$ and each
call requires $O(n)$ additional computation to combine the results,
leading to an $O(n^3)$ algorithm.

Efficiently computing this recurrence requires that intermediate
results be cached. In code:
\lstinputlisting[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]{Parens.java}

For the more general cases, we need to keep track of the minimum and maximum values
as well as the positive and negative values closest to zero.
This makes the code more complicated but does not change the
character and the complexity of the algorithm.

\ans{point-covering}
We schedule tutors greedily: as soon as there is a request that cannot be handled by the previously assigned tutors, we choose a new tutor.

While it is simple to implement this scheme, it is not completely
trivial to prove that it is
optimum, i.e., we cannot cover all the requests with fewer
tutors.

In order to prove the optimality we will define the notion of slack.
Consider a set of requests $j_1,\ldots,j_n$ such that the requests are ordered by the time
they need to be done. Let $S(j)$ and $E(j)$ be the starting and ending
time for request $j$.  Let $t_i,\ldots,t_m$ be the times when we assign a
tutor, ordered by time.
% It may be possible for the last tutor to be
% able to return after doing the last job and return in less than an
% hour.

Define the time the last tutor assigned has available after his last request
is fulfilled as the slack in the schedule.

We claim that greedy scheduling is the optimum scheduling.
We can prove this using induction over the number of requests;
for our induction hypothesis, in addition to the claim that
the number of tutors is minimized, we claim that the schedule maximizes the slack.
For $n=1$, the greedy algorithm will send exactly one tutor
at the start time of the request; clearly this is the strategy that uses the minimum number of tutors and no more slack is possible.

Let's assume that this statement is true for all values of $n \le k$.
 Now we can prove this for $n = k +1$ as follows: consider the requests
 $j_1,\ldots,j_k$ sorted by their start time. Consider that
 $t_1,\ldots,t_m$ are the times when we scheduled the tutors to cover
 these requests based on the greedy strategy. Now, when we add the next
 request $j_{k+1}$ to the list, either it can be covered by slack or
 it may require a new tutor.

In the case the new request can be
 covered by the slack, clearly this is the optimum solution (if we
 needed at least $m$ tutors to cover the first $k$ requests, we
 cannot cover the $k+1$ requests with fewer tutors). Also, in this case,
 since the schedule for the first $k$ requests
 maximized the slack, we cannot have a
 better schedule with $m$ tutors that cover all $k+1$ requests
 and have a bigger slack.

In case we need to pick an additional tutor for the $k+1$-th request,
it must be that the $m$-th
tutor did not have the slack to cover the last request. If there is
another way to cover the requests with $m$ or less tutors, then we can use
the same set of tutors to cover the first $k$ requests and get a bigger slack,
which contradicts our assumptions. Also, since the $(m+1)$-th tutor
will start exactly when the last request starts, this schedule must maximize the slack.

\ans{minimize-waiting-time}
Let's say that the time for the $i$-th customer to be serviced is $c_i$. Then the
waiting time for the customer $c_i$ would be $\sum_{j=1}^i t_{c_j}$.
Hence sum of all the wait times would be
\[\sum_{i=0}^n \sum_{j=1}^i t_{c_j} = \sum_{i=0}^n t_{c_i}\cdot i . \]

Since we want to minimize the total wait time for all the customers
and $c_i$s must take values from 1 through $n$, it follows that the
customers who take the smallest time must get served first. Hence we
must sort the customers by their service time and then serve them in
the order of increasing service time.

\ans{huffman-coding}
Huffman coding is an optimum solution to this problem (there could be
other optimum codes as well).  Huffman coding proceeds in three steps:
\begin{enumerate}
\itemsep 1pt
\item Sort symbols in increasing order of probability and create a
  binary tree node for each symbol.
\item Create a new node by combining the smallest probability
  nodes as children and assigning it the probability of the sum of its
  children.
\item Remove the children from consideration and add the new node into
  the sorted list of nodes to be combined and repeat the entire
  process till we have a single rooted binary tree.
\end{enumerate}

Once we have the rooted tree, we can assign all the left edges as 0
and the right edges as 1. All the original symbols would be the leaf nodes
in this tree and the path from root to the leaf node would give us the
bit sequence for that symbol.

Now, we need to prove (1.)~this encoding is optimum and (2.)~find
a fast implementation of this algorithm.

For implementing this idea, we can maintain a min-heap of candidate nodes
that can be combined in any given step. Since each combination step
requires two {\em extract-min} and one {\em insert} operation that can be
done in $O(\log n)$ time, we can find the Huffman codes in $O(n \log n)$
time.

We can prove the optimality of Huffman codes inductively. For a
single code, obviously Huffman codes are optimum. Let's say that for
any probability distribution among $n$ symbols, Huffman codes are
optimum. Given this assumption, we will prove it is true for
$n+1$.  Suppose there is another encoding that has a smaller expected
length of code  for some probability distribution for $n +1$
symbols.

 For any encoding, we can map the codes to a binary tree by
creating the null string to root and adding a left edge for each 0 and
a right edge for each 1. We can make several observations about this binary tree:
\begin{itemize}
\itemsep 1pt
\item Each symbol must map to a leaf node; otherwise, our prefix
  assumption will be violated.
\item There cannot be a nonleaf node that has less than two children
  (otherwise, we can delete the node and bring its child one level up
  and hence reduce the expected code length).
\item If we sort the binary tree leaves in order of their path lengths,
  the two longest paths must have the same length (since the
  parent of the leaf with the longest path must have another child).
\item The two nodes with the longest paths in the tree must be assigned to
  the two symbols with the smallest probability (otherwise, we can swap
  symbols and achieve smaller expected code length).
\item If we remove the two smallest probability symbols and replace them
  with one symbol that has its probability equal to the sum of the probabilities of the replaced symbols, the optimum prefix
  coding must have the same expected code length as this tree when we
  delete the two lowest probability nodes (otherwise, we can use this
  new optimum tree and replace it with the old tree).
\end{itemize}

Now consider that the symbols have probabilities $p_1 \le p_2 \le
\ldots \le p_{n+1}$.
Let $O(p_1,\ldots, p_{n+1})$ be the optimum
expected code length for this probability distribution and
$H(p_1,\ldots, p_{n+1})$ be the expected code length for Huffman coding. So, we can
easily see that
\ifthenelse{\boolean{createspace}}{
\[ O(p_1,\ldots, p_{n+1}) = O(p_1,\ldots, p_{n-1}, p_n + p_{n+1}) +
p_n + p_{n+1} . \]
}
{
\begin{eqnarray*}
\lefteqn{O(p_1,\ldots, p_{n+1}) =} \\
& & O(p_1,\ldots, p_{n-1}, p_n + p_{n+1}) + p_n + p_{n+1} .
\end{eqnarray*}
}
The way we construct Huffman codes we know that
\ifthenelse{\boolean{createspace}}{
\[ H(p_1,\ldots, p_{n+1}) = H(p_1,\ldots, p_{n-1}, p_n + p_{n+1}) +
p_n + p_{n+1} . \]
}
{
\begin{eqnarray*}
\lefteqn{ H(p_1,\ldots, p_{n+1}) = } \\
& & H(p_1,\ldots, p_{n-1}, p_n + p_{n+1}) +
p_n + p_{n+1} .
\end{eqnarray*}
}

By our inductive assumption,
\ifthenelse{\boolean{createspace}}{
$H(p_1,\ldots, p_{n-1}, p_n + p_{n+1}) =
O(p_1,\ldots, p_{n-1}, p_n + p_{n+1})$.
}
{
\begin{eqnarray*}
\lefteqn{ H(p_1,\ldots, p_{n-1}, p_n + p_{n+1}) = } \\
 & & O(p_1,\ldots, p_{n-1}, p_n + p_{n+1}).
\end{eqnarray*}
}
Hence $H(p_1,\ldots,
p_{n+1}) = O(p_1,\ldots, p_{n+1})$. In other words, Huffman coding
is optimum for $n+1$ symbols.

\ans{efficient-ui}
This problem is very similar in structure to the Huffman coding
problem above, if $c =1$. If we represent each click on the sub-menu
operation as a $1$ and each scan down operation as a $0$, then the path
to reach a menu item can be represented as a string of $0$s and $1$s.
The time it would take to reach each menu item is proportional to the
length of this string. And finally, we cannot have two actions mapped
to two strings such that one is a prefix of the other. Hence if we
use Huffman coding algorithm to come up with the bit-strings for each
action and then build the menu system based on these strings, we would
achieve the minimum expected time to interact with the menu.

When $c > 1$, it is similar to the case where there is an asymmetric cost
for a $0$ and a $1$ in the code (for example, it requires more power
to transmit a $1$ than a $0$). There is no known polynomial time
solution for this case. Below we describe an algorithm that will take $O(2^n \cdot n \cdot \log n)$ time:
\begin{enumerate}
\itemsep 1pt
\item Sort each operation by the probability of its occurrence.
\item Iterate over all possible binary tree structures with
  $n$ leaves. Map each left edge in the binary tree as a scan down
  operation and each right edge as a clock to open sub-menu operation.
\begin{enumerate}
\item For each leaf, compute the time it takes to reach the node
  (number of left edges + $c$ times the number of right edges in the path to the node).
\item Sort the nodes in the order of time it takes to reach it.
\item Map the actions to the nodes such that the highest probability
  action is mapped to the lowest visit time. Compute the expected visit
  time to the nodes.
\end{enumerate}
\item Find the tree structure that has the lowest expected time to
  visit.
\end{enumerate}
The number of unique binary trees with $n$ leaves is roughly $O(2^n)$.

\ans{first-fit}
This can be trivially done in $O(n^2)$ time if we do a linear scan for
the boxes for each new object to find the first box where it would fit.

In order to speed things up, we can maintain a list of boxes where a
certain capacity is available for the first time.  For each box, we
keep a record which contains the remaining box capacity and the box
number. We will maintain a sorted list of boxes, first by box capacity,
then by box number. When we receive a new item, we look for the first
record with capacity greater than or equal to the item's weight. We put it
in the corresponding box, update its capacity, and reinsert it at the
correct position. In order to maintain a sorted list, we can use a
balanced binary tree such that find, delete, and insert are all
$O(\log n)$ operations.

\ans{point-covering-1}
A covering set $S$ must contain at least one point $x$
such that $x \leq b_{min} = \min\{b_i\}$. Any such point covers the subset
of intervals $[a_i,b_i], a_i \leq b_{min}$. Of course, $b_{min}$ itself covers
all such intervals and so there exists a minimum cardinality covering
that contains $b_{min}$ and no other points to its left. Consequently,
the following procedure computes a minimum covering set $S$:

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
I = {1,2,...,n};
S = {};
while (I != {}) {
  bmin = min{b[i] | i in I}
  S = S + {bmin};
  I = I - {i|a[i] <= bmin};
}
\end{lstlisting}

Using a balanced BST, we can implement the search for minimum, insertion, and deletion in $O(\log n)$ time, yielding an $O(n\log n)$ algorithm.

\ans{point-covering-2}
If there is some point on the circle that is not contained in at least one
of the $n$ arcs, then the problem is identical to Problem~\ref{point-covering-1}.
So, suppose this is not so.  Without loss of generality, we may assume that a minimum
cardinality covering set $S$ contains only right endpoints of arcs, i.e., ``clockwise''
right endpoints. There are $n$ such endpoints.  If we choose a given right endpoint
and eliminate all the arcs that are covered by it, the remaining problem
is identical to that in Problem~\ref{point-covering-1}. This means we
can solve the arc-covering problem by $n$ calls to the algorithm
in Solution~\ref{point-covering-1}, yielding an $O(n^2 \log n)$
algorithm.

\ans{magazines}
Suppose our algorithm computes
the clustering $C = \{O_1,O_2,\ldots,O_k\}$.  Suppose $C$ is not optimum,
i.e., there is another clustering $P  = \{P_1,P_2,\ldots,P_k\}$ which lowers the separation.
Since $C$ and $P$ are distinct, there must be some pair of objects $a,b$
that are assigned to the same cluster in $C$ but different clusters in $P$---otherwise, either $C$ and $P$ would be identical or some $P_i$ would be empty, which was
explicitly disallowed.

Let $u,v$ be the last pair of objects that we merged in our algorithm.
Suppose $x,y$ were the next pair our algorithm would have merged if
we had performed one more iteration, i.e., computed a $k+1$-clustering.
Observe that $d(x,y)$ is the separation of $C$ since $x$ and $y$
are a pair of closest objects not in the same cluster.

Now, our algorithm has put $a$ and $b$ in the same cluster,
there is some set of pairs of the form $\{(a,\delta_1),
(\delta_1,\delta_2),\ldots,(\delta_{l-1},\delta_l), (\delta_l,b)\}$ that our algorithm selected.
(It may be that the set is simply $\{(a,b)\}$ if we directly selected $d(a,b)$.)
Since $a$ and $b$ are in different clusters in $P$, one of these pairs, call it $e$, must be
in distinct $P_i$ and $P_j$. Therefore the separation of $P$ is at most $d(e)$, which is
no more than $d(u,v)$. Now $d(u,v)$ is no more than $d(x,y)$, which is the
separation of $C$. Therefore the separation of $P$ is no more than that of $C$, contradicting the choice of $P$.
Therefore $C$ has the maximum separation of all $k$-clusterings.

\ans{party-planning}
We compute the optimum invitation list by iteratively removing
people who cannot meet Leona's constraints until there
is no one left to remove---the remaining set
is the unique maximum set of people that Leona can invite.

Specifically, we iteratively remove anyone who has fewer than six friends in the current set or anyone
who has fewer than six people they do not know in the current set.
The process must converge since we start with a finite number of
people and remove at least one person in each iteration.
The remaining set satisfies Leona's constraints by construction.

It remains to show that the remaining set is maximum.
In fact, we show something stronger, namely that it is the unique
maximum set that satisfies Leona's constraints.

We do this by proving that people who are removed could never be
in a set that satisfies the constraints.  We do this
by induction on the order in which the people were removed.

The first person $P_1$ removed was removed because either $P_1$ had fewer
than six friends in the entire set or the number of people
$P_1$ did not know was fewer than six---clearly $P_1$ cannot belong to any set that satisfies
the constraints, let alone the maximum set.

Inductively, assume the first $i-1$ persons removed could not belong to any set that satisfies
the constraints.

Consider $P_i$, the $i$-th person we remove. It must be that
either fewer than six people know $P_i$
in the current set or $P_i$  does not know fewer than six people in the current set.
But by induction, the current set includes any maximum set, so
the $i$-th person removed cannot belong to a maximum set and induction goes through.

\chapter{ Algorithms on Graphs}

\ans{maze}
Model the maze as an undirected graph.  Each vertex corresponds to a white pixel.
We will index the vertices based on the co-ordinates of the corresponding pixel; so, vertex $v_{i,j}$ corresponds to the matrix entry $(i,j)$.
Edges model adjacent pixels; so, $v_{i,j}$ is connected
to vertices $v_{i+1,j}$, $v_{i,j+1}$, $v_{i-1,j}$, and $v_{i,j-1}$, assuming these
vertices exist---vertex $v_{a,b}$ does not exist if the corresponding pixel is black
or the co-ordinates $(a,b)$ lie outside the image.

Now, run a DFS starting from the vertex corresponding to the entrance. If at some point, we discover the exit vertex in the DFS, then there exists a path from the entrance to the exit .
If we implement recursive DFS then the path would consist of all the vertices in the call stack corresponding to previous recursive calls to the DFS routine.

This problem can also be solved using BFS from the entrance vertex
on the same graph model.  The BFS tree has the property that the
computed path will be a shortest path from the entrance.  However
BFS is more difficult to implement than DFS since in DFS, the compiler implicitly
handles the DFS stack, whereas in BFS, the queue has to be explicitly
coded up.  Since the problem did not call for a shortest path,
it is better to use DFS.

\begin{comment}
Ian's solution

It is natural to apply graph models and algorithms to spatial problems. Consider a black
and white image of a maze: white pixels represent open areas and black spaces are walls.
There are two special pixels, one is designated the entrance and the other is the exit.

Given a two-dimensional matrix of black and white entries representing a maze, with
designated entrance and exit points, find a path from the entrance to the exit, if one
exists.

First, we need a routine to construct such mazes. In Python, we will deal with this input
maze as a two-dimensional array of characters (if we needed to preserve space, we could do
it with a two-dimensional bit matrix but this is fine for our purposes and will produce
clearer codes). When printed, such an array might use asterisks to show walls and spaces to
show paths. Of course, if we create an array with random gaps, it is very unlikely that
there will ever be a path through the maze, so we can do a little better: we can
initialize the maze to all walls, then ``carve'' paths in it that are runs of several
spaces, either vertically or horizontally.

\begin{verbatim}
import random
def CreateMaze(width, height):
    NUMPATHS = (width * height) / 2
    MAXPATHLENGTH = min(width, height) / 3
    maze = []
    for i in range(width):
        row = []
        for j in range (height):
            row.append("*")
        maze.append(row)
    # now randomly carve some paths
    for p in range (NUMPATHS):
        x = random.randint(0, width - 1)
        y = random.randint(0, height - 1)
        direction = random.randint(0,1)
        length = random.randint(0, MAXPATHLENGTH)
        CarvePath(maze, x, y, length, direction)
    return maze

def CarvePath(maze, x, y, length, direction):
    #editor.AddText("Carving path " + str(x) + ", " + str(y) + ", " + str(length) + ", " + str(direction) + "\n")
    if direction == 0:
        for xpos in range(x, min(x+length, len(maze))):
            maze[xpos][y] = " "
    else:
        for ypos in range(y, min(y+length, len(maze[x]))):
            maze[x][ypos] = " "

def PrintMaze(m):
    for row in m:
        for char in row:
            editor.AddText(char)
        editor.AddText("\n")
An example maze, of size 32 x 64, is shown here:

>> PrintMaze(CreateMaze(32, 64))

** **     *  **  **      ************* *******************    **
** ** **                  **   ******** **  **     ***       * *
** **  *  * **** ****    *** ***     ** *******               **
*      **** ****  **           * ******  *** ** ********** *  **
 *       ****      *      **      ** **  *** ** ** *  **** *  **
      * ***** **       * *** *    ** **  **  ** *    ***** ** **
        * *****    *     ***   *   * ***           ******* **  *
 * **      **        * *  **** *  ** ***      * *  ** ***  **
        * *** * *          *** *  * ** *  **        * ***    *
              ***  *         **** *             * ** ***   *
    *     *** *     *   *  *         * ****     * ******   * **
    *  *** ** *    ***       ***  * ** * * *     *         * **
 **        *       ***  *    **** * * ** ***   * * *****  ******
  **** * * *  **** **        **** * *    *** **  * *****  *****
   *****        ** **         *** *    ** **       * ***     * *
* * ****       ***    *****         * * * ** ** ** * ***  **
* *      **        ** *****  *     ** *** ****     * *    **   *
      * ***                         **     * **  * * **** *
* * ***             * * **          ****   *  *  *******     **
******        * *     * *      * *  ****      *          *
******       *      *   ***    * *  *  **         *    *     **
**              *   *   *** *       **    *   * ***    * ** ***
**  **          * *  *  ***                     ***  * *     **
**  **  **      *    *  *** *        **** * *   *** **** **
**  *             * **   ** ** *      **  * *    *  ****  ** **
 *        **  **  ** *  **         **                 **     **
 *  ** **         **         *** **       * **  ** *   *      *
 *  ******* * *  *      * *  ****  *           *   ** **  *****
  ****      * *   * *   * *        * *  * *******  ****   * **
    ******  * *  *  *     *               ** *****        *  **
     ** **               ******   *  *          ***  **       *
  ***** **  *** * **     **    *     **   **  * * *  *
\end{verbatim}

The constants for the number of paths and the maximum path length were determined empirically .
%(I
%just tried different numbers until I found some that seemed to make %mazes with long,
%winding paths through them at this size).
A more sophisticated approach would be to have
each path branch off the previous one with some probability but this will work for now.

Next, we have to create a graph representation of the open spaces that shows for every
open space what other spaces it connects to in the four cardinal directions. A good Python
data-structure for a graph is a dictionary (i.e., hash table) where the keys are locations
(in our case, $x/y$ co-ordinates) and the adjacency list is a list contained in the
dictionary's value. For example, in the maze above, the short ``hallway'' starting at
position (2, 0) and extending down might be represented as:

\begin{verbatim}
mazegraph = {
    (2, 0): [(2, 1)],
    (2, 1): [(2, 0), (2, 2)],
    (2, 2): [(2, 1), (2, 3)],
    etc.
}
\end{verbatim}

We can build a graph of the maze in this way pretty simply---go one location at a time,
from top left to bottom right, and build the adjacency dictionary.

\begin{verbatim}
def BuildMazeGraph(maze):
    mazegraph = {}
    for r in range(len(maze)):
        for c in range (len(maze[r])):
            # if it's open
            if maze[r][c] == " ":
                # add each of the four cardinal directions if empty and within maze
                if r < (len(maze) - 1):              #down
                    if maze[r+1][c] == " ":
                        addMazeNode(mazegraph, (r, c), ((r+1), c))
                if c < (len(maze[r]) - 1):           #right
                    if maze[r][c+1] == " ":
                        addMazeNode(mazegraph, (r, c), (r, (c+1)))
                if r > 0:                            #up
                    if maze[r-1][c] == " ":
                        addMazeNode(mazegraph, (r, c), ((r-1), c))
                if c > 0:                            #left
                    if maze[r][c-1] == " ":
                        addMazeNode(mazegraph, (r, c), (r, (c-1)))
    return mazegraph

def addMazeNode(mazegraph, k, v):
    if k not in mazegraph:
        mazegraph[k] = []
    mazegraph[k].append(v)
This builds a (nonconnected) graph data-structure from the maze:

>> len(BuildMazeGraph(CreateMaze(32, 64)).keys())

  = 1261
\end{verbatim}

Finding a path from any square to any other, if one exists, is as simple as doing a search
from the start to the end node using the graph-based version of the maze. In our case, we
have not yet designated a start and end square, and since we are generating the maze
randomly, we have to do that first. An easy choice is to take the first empty square
starting from the top left and make it the start, and likewise the last empty square from
the bottom right, and make it the end (this may make our mazes more difficult, i.e., less
likely to have a solution, but hopefully we have tweaked the creation algorithm to the point
where such paths happen occasionally, as they do in the generated example above).
End-point calculation is done like so:

\begin{verbatim}
def CalculateEntries(maze):
    # beginning and end are defined as the first and the last blank space
    start = end = ""
    for r in range(len(maze)):
        for c in range(len(maze[r])):
            if start == "" and maze[r][c] == " ":
                start = (r, c)
            if maze[r][c] == " ":
                end = (r, c)
    return start, end
\end{verbatim}

From there, we solve the maze by doing a search (a depth-first search in this case) for a
path from the start to the end:

\begin{verbatim}
def FindPath(mazegraph, start, end, path=[]):
    mypath = path[:] # make a copy of the incoming path, do not change it by reference
    mypath.append(start)
    if start == end:
        return mypath
    if not mazegraph.has_key(start):
        return None
    for node in mazegraph[start]:
        if node not in mypath:
            newpath = FindPath(mazegraph, node, end, mypath)
            if newpath: return newpath
    return None
\end{verbatim}

We can now use a function that marks the maze with each item on the found path. Rather
than using a single character, using a counter that ranges from 0 to 9 can give us a
better feel for what path was actually taken. Together:

\begin{verbatim}
def MarkPath(maze, path):
    if path != None:
        counter = 0
        for node in path:
            maze[node[0]][node[1]] = str(counter % 10)
            counter = counter + 1
    return maze

def SolveMaze(maze):
    start, end = CalculateEntries(maze)
    mazegraph = BuildMazeGraph(maze)
    return MarkPath(maze, FindPath(mazegraph, start, end))
\end{verbatim}

This seems to work, and returns solved mazes some of the time:

\begin{verbatim}
>> PrintMaze(SolveMaze(CreateMaze(10, 20)))

*0  ******** *******
 1 * *******     *
 290123**** * ****
 38***4     *  ****
*47***5**** ** **
 56** 6789  *  *
* *** ***012  * ****
  *** *** *3    *
* **   ** *45**  *
*** *  *****67890123
\end{verbatim}

However the time to do this is quite variable depending on the exact layout of
the maze---some larger mazes (say, $15\times 15$) with many paths and no
solutions can take several minutes
to return a solution. This could be fixed by better use of memory, tighter algorithms,
etc.

Note also that it is not the shortest path (in the example above, it goes all the way down
to step five (position $(1, 5)$) before turning up again).
\end{comment}

\ans{link-binary-tree-nodes}
If you traverse the binary tree in BFS order,
then you are guaranteed to hit all the nodes at the same depth
consecutively. So, you can build the linked list of all the nodes as you
discover them in BFS order. While traversing the tree, we also need to know when we move from
nodes of depth $k$ to nodes of depth $k + 1$. This can be easily
achieved by keeping track of the depth when inserting nodes in
the queue.

\ans{conn}
%Let's say $G$ has Property~$C0$ iff there exists an edge $e$ such
%that $G' = (V, E-\{e\})$ is connected.
First, we consider the problem of checking if
$G$ is $2\exists$-connected.
If $G' = (V, E- \{(u,v)\})$ is connected, it must be that a path exists between $u$ and $v$.
This is possible iff $u$ and $v$ lie on a cycle in $G$.  Thus $G$ is $2\exists$-connected
iff there exists a cycle in $G$.

We can check for the existence of a cycle in $G$ by running DFS on $G$.  As soon as
we discover an  edge from a gray vertex back to a gray vertex which is not its immediate
predecessor in the search, a cycle exists in $G$ and we can stop.

The complexity of DFS is $(|V| + |E|)$; however in the case described above,
the algorithm runs in $O(|V|)$ time. This is because an undirected
graph with no cycles can have at most $|V|-1$ edges.

%Let's say $G$ has Property~$C1$ iff for all edges $e$
% $G' = (V, E-\{e\})$ is connected.

Now, we consider the problem of checking if $G$ is $2\forall$-connected.
 Clearly, $G$ is not $2\forall$-connected iff there exists an edge $e$
 such that $G' = (V, E-\{e\})$ is disconnected.
 The latter condition holds iff there is no cycle including edge $e$.

 We can find an edge $(u,v)$ that is not on a cycle with DFS.
 Without loss of generality, assume $u$ is discovered first.
 Observe that the removal of $(u,v)$ disconnects $G$ iff there are no back-edges
 between $v$ or $v$'s descendants to $u$ or $u$'s ancestors.

 Define $l(v)$ to be the minimum of the discovery time $d(v)$ of $v$ and
 $d(w)$ for $w$ such that $(t,w)$ is a back-edge from $t$, where $t$ is a
 descendant of $v$.

 We claim $l(v) < d(v)$ iff there is a back-edge between $v$ or one of $v$'s descendants
 to $u$ or one of $u$'s ancestors.
 If $l(v) < d(v)$, then there is a path from $v$ through one of its descendants
 to an ancestor of $v$, i.e., $v$ lies on a cycle.
 If $l(v) = d(v)$, there is no way to get from $v$ back to $u$; hence
 removal of $(u,v)$ disconnects $u$ and $v$.

 Now, we show how to compute $l(v)$ efficiently: once we have processed all of $v$'s children,
 then $l(v) = \min\big(d(v), \min_{x \, \mbox{\scriptsize child of } \,v} l(x) \big)$.  This computation
 does not add to the asymptotic complexity of DFS since it is just a constant
 additional work per edge, so we can check $2\forall$-connectedness in linear-time.

\ans{wiring}
Assuming the pins are numbered from $0$ to $p-1$,
create an undirected graph $G$ on $p$ vertices $v_0,\ldots,v_{p-1}$.
Add an edge between $v_i$ to $v_j$ if pins~$i$~and~$j$ are connected by a wire.

Assume for simplicity, $G$ is connected; if not, the connected components can
be analyzed independently.

Run BFS on $G$ starting with $v_0$. Assign $v_0$ arbitrarily to lie on the left
half.  All vertices at an odd distance from $v_0$ are assigned to the right half.

When performing BFS on an undirected graph, all newly discovered edges will
either be from vertices which are at a distance $d$ from $v_0$ to undiscovered vertices (which will
then be at a distance $d+1$ from $v_0$) or from vertices which are at a
distance $d$ to vertices which are also at a distance $d$. First, assume we never encounter an edge from
a distance $k$ vertex to a distance $k$ vertex. In this case,
each wire is from a distance $k$ vertex to a distance $k+1$ vertex, so all wires are between
the left and right halves.

If any edge is from a distance $k$ vertex to a distance $k$ vertex,
we stop---the pins cannot be partitioned into left and right halves as desired.
The reason is as follows:  let $u$ and $v$ be
such vertices. Consider the first common ancestor $a$ in the BFS search of $u$ and $v$ (such an
ancestor must exist since the search started at $v_0$).  The paths $p_{a,u}$ and $p_{a,v}$ in the BFS tree from $a$ to $u$ and $v$ are
of equal length; therefore the cycle formed by going from $a$ to $u$, then through the edge $(u,v)$, and then back to $a$ from $u$ via
$p_{a,v}$ has an odd length.  The vertices in an odd length cycle cannot be partitioned into two sets such that all edges
are between the sets.

\ans{tc}
It is natural to model the network as a graph: vertices correspond
to individuals and an edge exists from $A$ to $B$ if $B$ is a contact of $A$.

For an individual $x$, we can compute the set of $x$'s contacts by running graph search
(DFS or BFS) from $x$.  Running graph search for each individual leads
to a $O\big(| V |\cdot ( |V| +|E|)\big)$ algorithm for transitive closure.

Another approach which has complexity $O(|V|^3)$ but which may be
more efficient for dense graphs is to run an all-pairs shortest path algorithm
with edge weights of 1.  If there is a path from $u$ to $v$, the
shortest path distance from $u$ to $v$ will be finite; otherwise, it
will be $\infty$.  We can further improve the shortest path calculation
by simply recording whether there is a path from $u$ to $v$ or not; in this
way, we need a Boolean matrix rather than an integer matrix encoding the
distances between the vertices.

\ans{euler}
Let $v$ be any vertex in $G$.
Consider an Euler tour $T$ of $G$.
Each time the tour enters $v$, it must exit $v$ by a different edge.  Furthermore,
each edge must be entered exactly once and exited exactly once.  Hence
we can put incoming edges and outgoing edges in a 1-1 correspondence, so
the in-degree and out-degree of $v$ must be equal.

Conversely, let the in-degree and out-degree of every vertex $v$ in $G$ be equal.
Construct an Euler tour as follows:
start with an arbitrary vertex.  Use DFS to explore from this vertex until a simple cycle is found.
Such a cycle must exist since we can never get trapped in a vertex---if we entered a newly discovered vertex, we can
always exit it because of the constraint that in-degree equals out-degree.

Continue doing this till all the edges have been partitioned into disjoint simple cycles.  Now, merge these cycles as follows: start with any cycle.  For any vertex on the
current cycle, find a cycle that it is in, which is not the current cycle, and add a detour to this new cycle.
Iteratively add cycles to the current cycle.

We claim that all disjoint cycles must be merged by this process.
If not, there must be an edge $(p,q)$ on a simple cycle $S$ that is not in
 the cycle $C$ our process has converged to, where   $p$ appears in $C$ (such
an edge exists because the graph is connected).  We can merge the edges of $S$ to our cycle
about $p$, thereby contradicting the maximality of $C$.

The algorithm for constructing the cycles is just DFS and the merge process is also linear-time, so the algorithm is linear-time.

\ans{eph}
Model the FSM as a graph---each state $s$ corresponds to a distinct vertex $v_s$.
The edge set consists of precisely those edges
which correspond to potential
transitions between states;
specifically, $(v_s,v_t) \in E$ iff $\exists i \, T(s,i) = t$.  We will refer to states
and vertices interchangeably.

Now, consider the directed acyclic graph (DAG) of strongly connected components (SCCs) for this graph.  Any state not in an SCC (which is the sink of this DAG)
may transition out of the SCC which it is in and once it is out, it will not return.
Conversely, all states within the sink SCCs can return to themselves, so the states in the
sink SCCs are precisely the nonephemeral states; the complement  of this set is the desired set of ephemeral states.

The SCC DAG of a graph can be computed in linear-time from the
graph model and the graph itself can be constructed in linear-time from the FSM, so the whole computation is linear.

\ans{tree-diam}
We can compute the diameter by running BFS from each vertex and recording the largest shortest path distance. This has
$O\big(|V|\cdot(|V|+|E|)\big) = O(|V|^2)$ complexity since $|E| = |V|-1$
in a tree.

We can achieve better time complexity by using divide-and-conquer.
Let $r$ be any vertex. We take $r$ to be the root of the tree $T$.
Suppose $r$ has degree $m$ and
the subtrees rooted at $r$'s children are $T_1,T_2,\ldots,T_m$. Let
$d_1,d_2,\ldots,d_m$ be their diameters and $h_1,h_2,\ldots,h_n$ their heights.

Let $\lambda$ be a longest path in $T$. Either it
passes through $r$ or it does not.
If it does not pass through $r$, it must be entirely within one of the $m$ subtrees and hence the longest path length in $T$ is the maximum of $d_1,d_2,\ldots,d_m$.
If it does pass through $r$, it must be between a pair of vertices in distinct subtrees
that are farthest from $r$. The distance from $r$ to the vertex in $T_i$ that is farthest
from it is simply $f_i = h_i + 1$.
The longest length path in $T$ is the larger of the maximum of $d_1,d_2,\ldots,d_m$ and the two largest $f_i$s.

If we process the subtrees one at a time,
update $\max_i\{d_1,d_2,\ldots,d_i\}$, and the largest and second
largest of the $f_is$, the time complexity is proportional to the
size of the tree, i.e., $O(|V|)$.

\ans{timing-analysis}
Assume the inputs to the network stabilize at time $0$.
We are trying to bound when the primary outputs stabilize.

Suppose gate $g$ has a delay $D(g)$.  It will stabilize at
no more  than $D(g)$ time after all its inputs have stabilized.
Therefore we can compute when each gate has stabilized by processing gates
in topological order, starting from the primary inputs---for each gate,
we can bound when it stabilizes since we have already bounded when its
inputs have stabilized.  Topological ordering for a graph
can be computed in $O(n+m)$
time, where $n$ and $m$ are the number of vertices and edges in the graph.

The value we compute is an upper bound and may not be tight because of
logical relationships between signals---for example, if one of the inputs to an
AND gate is 0, then the output of the AND gate will be independent of the changes at its other inputs.

\ans{team-photo-1}
Let $A$ and $B$ be $n$-dimension real vectors;
write $A < B$ if $A[i] < B[i]$ for each $i$.
The $<$ relation is transitive.

Let $\langle x_1,x_2,\ldots,x_{20}\rangle$ be the heights of
the players in Team~X and $\langle y_1,y_2,\ldots,y_{20}\rangle$
be the heights of the players in Team~Y.
The key observation
is that Team~X can be placed in front of Team~Y
iff $\mbox{\textsc{sort}}\langle x_1,\ldots,x_{20}\rangle < \mbox{\textsc{sort}}\langle y_1,\ldots,y_{20}\rangle$.

Now, we define a DAG $G$ with vertices corresponding
to the teams as follows: there is an edge from vertex $X$ to $Y$ iff
$\mbox{\textsc{sort}}(X) < \mbox{\textsc{sort}}(Y)$.

Every sequence of teams where the successive teams can
be placed in front of each other corresponds to a path in $G$.
To find the longest such sequence, we simply need to find the longest
path in the DAG $G$. We can do this, for example, by topologically
ordering the vertices in $G$; the longest path terminating at vertex
$v$ is the maximum of the longest paths terminating at $v$'s fanins
concatenated with $v$ itself.

The topological ordering computation is $O(|V| + |E|)$
and dominates the computation time.

% Use Dilworth's theorem decomposition into chains

\ans{radio-freq}
The most obvious approach is to start with an arbitrary two-coloring.
If it is diverse, we are done.

At this point, a natural approach  would be to look  for a nondiverse vertex $v$
and flipping $v$'s color but this can result in some of $v$'s
neighbors becoming nondiverse.

To prove that this approach works, we look at {\em diverse} edges---edges between
vertices of different colors.
We claim that a coloring that maximizes the number of diverse edges is also
diverse.

If not, suppose $x$ is not diverse.  Without loss of
generality, suppose $x$ is white. Then by changing $x$'s color to black,
the number of diverse edges strictly
increases (since $x$ had more white  neighbors than black neighbors).

Therefore a coloring which maximizes the number of diverse edges
yields a diverse graph. Such a coloring must exist: because the
graph is finite, there are only a finite number of colorings.
We can construct a coloring by starting with an arbitrary coloring
and applying the argument above, i.e., finding nondiverse vertices
and flipping their color.

\ans{sp-min-edge}
Usually Dijkstra's shortest path algorithm uses scalar values for edge
length. However it can easily be modified  to the case
where edge weight is a vector if {\em addition} and {\em comparison} can be defined over
the vectors. In this case, if the edge cost  is $c$, we
say the length of the edge is given by the vector  $\langle c, 1\rangle$. We define addition to be
just component-wise addition. Hence if we sum up the edge lengths over a
path, we essentially get the total cost and the number of edges in the
path. The compare function can be just the lexicographic (first by the total cost, then by the number of edges). With this, we can run Dijkstra's shortest path algorithm and find the shortest path that
requires the least number of edges.

\begin{comment}
A key step in Dijkstra's algorithm for single-source shortest paths
involves updating the shortest path estimates as the vertices are discovered.

Instead of storing shortest path length estimates, we can store
a shortest path estimate and the fewest number of edges
for this estimate.  We will record these in two arrays $d[x]$ and $w[x]$.

The process of relaxing the edge $(r,s)$
then updates the shortest path estimate as follows: if $d[r] + d(r,s) < d[s]$, then
$d[s] = d[r] + d(r,s)$ and $w[s] = w[r] + 1$; if  $d[r] + d(r,s) < d[s]$,
nothing  is updated;  and if  $d[r] + d(r,s) = d[s]$ and
$w[s] > w[r] + 1$, $w[s] = w[r] + 1$.

This has the same complexity as Dijkstra's algorithm but keeps track of
the fewest edges on a shortest path.
\end{comment}

\ans{number-sp}
We can compute the number of shortest paths by
performing a BFS-type computation starting at $u$.

Consider the set of vertices $S_{k-1}$ such that for any vertex $a \in S_{k-1}$, the shortest
 distance between $u$ and $a$ is $k-1$. Now, consider a vertex $v$ such that the shortest distance
 between $u$ and $v$ is $k$. If we know the number of shortest paths between $u$ and any vertex in
 $S_{k-1}$, we can easily infer the number of shortest paths between $u$ and $v$ by summing up this
 number for all vertices $a \in S_{k-1}$ that also have an edge to $v$.  This is because each
 distinct path from $u$ to $a$ also gives us a distinct path from $u$ to $v$ by simply adding the
 edge from $a$ to $y$ to the path.

BFS runs in linear-time and, assuming we store the number of shortest paths
from intermediate vertices, the computation for a distance $k$ node
is proportional to the number of its outgoing edges. Hence the complete
algorithm runs in linear-time.

\begin{comment}

Ian's solution---

Most of the problem we've considered involve finding a single optimum solution. Sometimes
we may want to know the number of optimum solutions. For example, there may be multiple
shortest paths between two vertices of a graph.

Develop a linear-time algorithm that computes the number of shortest paths from a vertex u
to a vertex v in an undirected graph, where each edge weight is one.

The brute force algorithm for finding shortest paths (i.e. compute all possible paths)
would give us an easy way to determine the number of optimal solutions but it is clearly
not linear-time.

When we use a breadth-first search to compute shortest path, it is linear but we typically
only get one shortest path - the algorithm explicitly ignores other possible paths,
leaving a tree with only one path from the root to any node. However, if we store depths
(aka path lengths) as well as found count as we traverse the graph, any time we
encounter a node, we can see if the depth we're encountering it at now is the same as the
shortest depth, and if so, increment the number of optimal paths counter. Then we simply
look at this number for the target:

We can start by making a simple undirected graph G(v,e) as:
\begin{verbatim}
def MakeUndirectedGraph(v, e):
    # create an undirected graph of v nodes with e edges
    graph = {}
    for i in range(v):
        graph[i] = []
    while e > 0:
        v1, v2 = random.randint(0, (v - 1)), random.randint(0, (v - 1))
        if (v1 != v2) and v2 not in graph[v1]:
            graph[v1].append(v2)
            graph[v2].append(v1)
        e = e - 1
    return graph
\end{verbatim}

We can then use our variant of the standard iterative BFS algorithm, keeping a data
structure with the best depth and found count as we go:

\begin{verbatim}
def NumShortestPaths(graph, source, target):
    nodes = {} # a hash table for storing the depth and found count, for each node
    queue = [] # a queue for BFS processing, which also stores the depth
    current_depth = 0
    queue.append((source, current_depth))
    nodes[source] = (current_depth, 1) # tuple of depth & found count

    while queue != []:
        current, current_depth = queue.pop(0) # get the next node to process in BFS
        for neighbor in graph[current]:
            if neighbor not in nodes:
                queue.append((neighbor, current_depth + 1))
                nodes[neighbor] = (current_depth + 1, 1)
            else:
                node_depth, node_found_count = nodes[neighbor]
                if current_depth + 1 == node_depth: # in BFS, can never find a shorter, but can find same
                    nodes[neighbor] = (node_depth, node_found_count + 1)

    return nodes[target][1]
\end{verbatim}

A run shows:

\begin{verbatim}
>> NumShortestPaths(MakeUndirectedGraph(20, 40), 0, 19)

 = 2
The debug output for this run shows that for this graph:

{
    0: [16, 7, 18, 2, 17],
    1: [15],
    2: [6, 0, 12],
    3: [8, 17, 18, 19],
    4: [16, 15, 12],
    5: [17, 11, 12, 15],
    6: [2, 14],
    7: [0, 17],
    8: [9, 19, 3, 11, 14],
    9: [8],
    10: [15],
    11: [12, 15, 8, 5, 18],
    12: [18, 2, 13, 11, 17, 5, 4],
    13: [15, 12],
    14: [6, 8],
    15: [13, 10, 16, 11, 18, 1, 4, 19, 5],
    16: [0, 15, 4],
    17: [7, 5, 0, 3, 12],
    18: [12, 0, 3, 15, 11],
    19: [8, 15, 3]
}
\end{verbatim}
The two shortest paths to 19 are:

\begin{verbatim}
0 -> 16 -> 15 -> 19
0 -> 17 -> 3  -> 19
\end{verbatim}

The processing tree looked like this (indented by depth):
\begin{verbatim}
Popped off queue: node 0, depth 0
    Added to queue: node 16, depth 1, found count 1
    Added to queue: node 7, depth 1, found count 1
    Added to queue: node 18, depth 1, found count 1
    Added to queue: node 2, depth 1, found count 1
    Added to queue: node 17, depth 1, found count 1
    Popped off queue: node 16, depth 1
        Added to queue: node 15, depth 2, found count 1
        Added to queue: node 4, depth 2, found count 1
    Popped off queue: node 7, depth 1
    Popped off queue: node 18, depth 1
        Added to queue: node 12, depth 2, found count 1
        Added to queue: node 3, depth 2, found count 1
        Incremented in queue: node 15, depth 2, new count 2
        Added to queue: node 11, depth 2, found count 1
    Popped off queue: node 2, depth 1
        Added to queue: node 6, depth 2, found count 1
        Incremented in queue: node 12, depth 2, new count 2
    Popped off queue: node 17, depth 1
        Added to queue: node 5, depth 2, found count 1
        Incremented in queue: node 3, depth 2, new count 2
        Incremented in queue: node 12, depth 2, new count 3
        Popped off queue: node 15, depth 2
            Added to queue: node 13, depth 3, found count 1
            Added to queue: node 10, depth 3, found count 1
            Added to queue: node 1, depth 3, found count 1
            Added to queue: node 19, depth 3, found count 1
        Popped off queue: node 4, depth 2
        Popped off queue: node 12, depth 2
            Incremented in queue: node 13, depth 3, new count 2
        Popped off queue: node 3, depth 2
            Added to queue: node 8, depth 3, found count 1
            Incremented in queue: node 19, depth 3, new count 2
        Popped off queue: node 11, depth 2
            Incremented in queue: node 8, depth 3, new count 2
        Popped off queue: node 6, depth 2
            Added to queue: node 14, depth 3, found count 1
        Popped off queue: node 5, depth 2
            Popped off queue: node 13, depth 3
            Popped off queue: node 10, depth 3
            Popped off queue: node 1, depth 3
            Popped off queue: node 19, depth 3
            Popped off queue: node 8, depth 3
                Added to queue: node 9, depth 4, found count 1
            Popped off queue: node 14, depth 3
                Popped off queue: node 9, depth 4
\end{verbatim}

\end{comment}

\ans{rand-dag-path}
% The proof of NP-completeness was given by
% http://www.google.com/url?sa=t&source=web&ct=res&cd=2&ved=0CB8QFjAB&url=http%3A%2F%2Fciteseerx.ist.psu.edu%2Fviewdoc%2Fdownload%3Fdoi%3D10.1.1.78.3513%26rep%3Drep1%26type%3Dpdf&ei=rgz-S97KD4T78Aar78zQDQ&usg=AFQjCNHXvyetloBHkO7iOZLz0tb0BbLuhQ&sig2=Gu3sEXprs9xPLiYLybbc2A
%
% Bicriteria Shortest Path Problems in the Plane
This is an NP-complete problem and hence there is no efficient algorithm known
for it. However if the probabilities assigned to each edge come from  a small set of numbers or if we
are willing to approximate the probabilities, then this can be solved efficiently.

It is natural to solve this problem using dynamic programming---we iteratively compute
the matrix $M^{k}_p(s,t)$ which is the shortest path distance between vertices $s$ and $t$
such that the probability of a path existing with that distance is at least $p$ and the
number of edges in the path is exactly $k$.

Given $M^k_p(s,t)$, we can compute $M^{k+1}_p(s,t)$ using the recurrence
\ifthenelse{\boolean{createspace}}{
\[
M^{k+1}_p(s,t) = \min{p'}\min_{u \, \in\,  \mbox{\scriptsize fanin}(t)}\big(M^{k}_{p'}(s,u) \cdot M^{1}_{p/p'}(u,t)\big) .
\]
}
{
\begin{eqnarray*}
\lefteqn{M^{k+1}_p(s,t) = } \\
 & & \min{p'}\min_{u \, \in\,  \mbox{\scriptsize fanin}(t)}\big(M^{k}_{p'}(s,u) \cdot M^{1}_{p/p'}(u,t)\big) .
\end{eqnarray*}
}
There are an infinite number of values for $p$: any real number in $[0,1]$.
In reality, there are only a finite number of paths, so we only need to   consider
those probabilities. However the number of paths in a graph can be exponential
and each path can have a distinct probability, so it is not realistic to
consider the possible set of values for $p$. Instead, we can take the approach
of binning: we compute $M^{k}_{p}$  for a range of values for $p$, e.g.,
$p = \frac{n}{100}, \; n = 0\, \mbox{to} \, 100$.

\ans{rand-dag}
Let's model the map  as a graph $G=(V,E)$ such that each room $i$ is
represented by vertex $v_i\in V$ and an edge $(v_,v_j)\in E$ exists
iff there is a way to go from room $i$ to room $j$. Let
$l(e)$ be the length of the corridor represented by the edge $e$.

The key idea here is to assign each room an expected time to the
treasure room
when we follow the optimal strategy. Let's say for room $i$, the
expected time to the treasure room is $t(i)$. Then for a nonspecial room $i$, we
would always pick the next room to be the one that gives us the
smallest expected time to the treasure room. Hence for nonspecial room $i$
\[ t(i) = \min_{j:(i,j) \in E} \Big(l\big((i,j)\big) + t(j)\Big) . \]
On the other hand, for the special rooms, the expected time is going to
be the average of the expected times through all the outgoing edges. Hence for special room $i$
\[ t(i) = \mbox{avg}_{j:(i,j) \in E} \Big(l\big((i,j)\big) + t(j)\Big) . \]
Also, if the treasure room is vertex $s$, then $t(s)=0$. Using these
relationships, we can compute $t(i)$ for each vertex $i$ by
initializing $t(i) = \infty$ for all nodes $i\not=s$ and $t(s)= 0$.
Then we apply the relaxation for each node based on one of the two
above equations. Since this graph is a DAG, after $|V|$ steps of
relaxation, we would reach a fixed point. This algorithm would have a
runtime of $O(|E|\cdot|V|)$ since each relaxation phase takes $|E|$
time. This can be further improved by inverting the graph, doing a
topological sort of the graph by starting at node $s$, and then
computing $t(i)$ for node $i$ in topological order.

Once we have all the values of $t$ computed, if we are in any room where
we have to make a choice, we choose the corridor that minimizes the
expected time to the treasure room.

\begin{comment}
From Ian Varley

Base case: trivial.  1 hop---either go to treasure room in one hop or go to a special room,
know the prob that the special room will work.  2 hop---go deterministically, get 1-hop answer,
or go to a special room, get 1-hop answer.

The difference between this and our maze in the previous problem is that this graph is now
directed - you can only pass from one room to another in one direction. We also no longer
have the expectation of a square matrix - rooms can have any number of passages and can
already connect to any other rooms anywhere in the maze. Per clarification via email, this
graph is also acyclic, so there are no back links or two way corridors.

This can be simulated simply by creating a set of N random  nodes, then walking through the
nodes a single time and making one or more randomized forward connections from each node
(starting at a random node and proceeding through every node, modulo the number of nodes).
The result is a directed acyclic graph. Each room is labeled with a random number between
0 and (n-1).

\begin{verbatim}
# create a graph with n nodes and up to c directed connections per node
def CreateRandMaze(n, c):
    # create graph
    mazegraph = {}
    for i in range(n):
        mazegraph[i] = []

    # add edges between nodes: start at any node, cycle through
    # remaining nodes, adding forward connections only
    #offset = random.randint(0, (n-1))
    offset = 0
    for j in range(n):
        m = (offset + j) % n
        # insert a random number of random forward links
        for k in range(random.randint(0, c)):
            dest = (offset + random.randint(j, (n-1))) % n
            if dest != m and dest not in mazegraph[m] and dest in mazegraph:
                mazegraph[m].append(dest)

    return mazegraph
\end{verbatim}

This creates simple directed acyclic graphs as in the following case (where ``offset'' was
randomly chosen to be zero):

\begin{verbatim}
>> CreateRandMaze(10,20)

{
    0: [1, 6, 2],
    1: [7, 4],
    2: [7, 8, 5],
    3: [5, 8, 9],
    4: [8, 9, 7, 5],
    5: [6, 9, 7, 8],
    6: [8, 7],
    7: [9, 8],
    8: [],
    9: []
}
\end{verbatim}

Note that there are actually two nodes with no incoming links: 0 and 3. We will pick those
nodes to be our starting rooms and then randomly pick one node with no outgoing links as
our treasure room. Our first pass at the algorithm should return the shortest path from
the start rooms to the treasure; later, we will modify that to deal with special rooms.

We can easily find the shortest path using a recursive BFS or DFS that returns the path
length and node list. This is an unweighted graph, so each edge costs 1. Note that this
simple version of ShortestPath only works in acyclic graphs - for graphs with possible
cycles, a more robust algorithm like Dijkstra's algorithm should be used. Since there can
be multiple entrances, our return has the following structure:

\begin{verbatim}
    { # maze graph:
        room: [exits ...],
        room: [exits ...],
        ...
    },
    { # solution: for each entrance
        entrance: (length, [path ...]),
        entrance: (length, [path ...]),
        ...
    }
def SolveDirectedMaze(mazegraph):
    # solve the shortest path in the maze between a chosen start and end node
    paths = {}
    treasure = len(mazegraph) - 1
    for entrance in GetEntrances(mazegraph):
        paths[entrance] = ShortestPath(mazegraph, entrance, treasure)
    return mazegraph, paths

def ShortestPath(graph, start, end):
    if start == end: return 0, [end]
    sl, sp = len(graph) + 1, None
    for dest in graph[start]:
        l, p = ShortestPath(graph, dest, end)
        if l < sl:
            sl = l
            sp = p
    if sp != None:
        sp.insert(0, start)
    return sl + 1, sp

def GetEntrances(mazegraph):
    possibles = {}
    for room in mazegraph.keys():
        possibles[room] = True
    for room in mazegraph:
       for dest in mazegraph[room]:
            possibles[dest] = False
    entrances = []
    for room in possibles.keys():
        if possibles[room] == True and len(mazegraph[room]) > 0:
	    # entrances must have at least one exit
            entrances.append(room)
    return entrances

>> SolveDirectedMaze(CreateRandMaze(14, 5))

 = (
    {
        0: [9, 13, 2],
        1: [4, 7, 9],
        2: [12],
        3: [7, 11],
        4: [10, 12, 8, 9],
        5: [],
        6: [12],
        7: [8, 9],
        8: [9],
        9: [11, 13, 10],
        10: [11],
        11: [13, 12],
        12: [13],
        13: []
    },
    {
        0: (1, [0, 13]),
        1: (2, [1, 9, 13]),
        3: (2, [3, 11, 13]),
        5: (16, None),
        6: (2, [6, 12, 13])
    }
)
\end{verbatim}

How should we alter this strategy in the face of special rooms? We are still solving a
shortest path problem; however we need to deal with the cost of hitting a special node
differently than a regular node. When asked for the cost of a path from a special room to
the treasure, instead of returning the min path, it should return an average of its paths.

If the treasure is not reachable from all nodes, the length of the shortest path  from that node may be infinity. While writing programs, this may present a problem and usually substituting infinity with a very large number works well.

We can make the maze ``special'' by passing it through a function that simply adds a special
token to the edge list of f randomly selected nodes:

\begin{verbatim}
# add a marker to the edge list of a random set of f nodes
def Randomize(mazegraph, f):
    SPECIAL = -1

    # choose a set of f nodes
    rooms = mazegraph.keys()[:]
    specialnodes = []
    popout = 1000 # there may be cases where we get unlucky, and the # of branching rooms is < f
    while f > 0 and popout > 0:
        specialnode = random.randint(0, len(rooms) - 1)
        if len(mazegraph[rooms[specialnode]]) > 1: # only make branching rooms special
            specialnodes.append(rooms[specialnode])
            del(rooms[specialnode])
            f = f - 1
        popout = popout - 1
    specialnodes.sort()

    # add marker to chosen rooms
    for m in specialnodes:
        mazegraph[m].append(SPECIAL)
    return mazegraph
\end{verbatim}

Our solution then changes only in that we return an average instead of the min:

\begin{verbatim}
def SolveDirectedRandomMaze(randommazegraph):
    # solve the shortest path in the maze between a chosen start and end node
    paths = {}
    treasure = len(randommazegraph) - 1
    for entrance in GetEntrances(randommazegraph):
        paths[entrance] = RandomShortestPath(randommazegraph, entrance, treasure)
    return randommazegraph, paths

def RandomShortestPath(graph, start, end):

    MAX = len(graph) * 2 # some slightly large number, can be tweaked
    SPECIAL = -1

    if start == end:
        return 0, [end]
    if len(graph[start]) == 0:
        return 0, None
    if SPECIAL in graph[start]:
        # calculate average
        totallength, numpaths, possiblepaths = 0, 0, []
        for dest in graph[start]:
            if dest != SPECIAL:
                l, p = RandomShortestPath(graph, dest, end)
                numpaths += 1
                if p == None:
                    totallength += MAX
                else:
                    totallength += l
                possiblepaths.append(p)
        if numpaths > 0:
            averagelength = float(totallength) / float(numpaths)
            if possiblepaths != None:
                possiblepaths.insert(0, start)
                possiblepaths.insert(0, 'Random (' + str(averagelength + 1) + '):')
            return averagelength + 1, (possiblepaths)
        else:
            return 0, None
    else:
        # calculate min
        sl, sp = MAX, None
        for dest in graph[start]:
            if dest != SPECIAL:
                l, p = RandomShortestPath(graph, dest, end)
                if p != None and l < sl:
                    sl = l
                    sp = p
        if sp != None:
            sp.insert(0, start)
        if sp == [] or sp == None:
            return 0, None
        else:
            return sl + 1, sp

def Pretty(list):
    mazegraph = list[0]
    editor.AddText("\n{\n")
    for key in mazegraph:
        editor.AddText("    " + str(key) + ": " + str(mazegraph[key]) + "\n")
    editor.AddText("}\n")
    solutions = list[1]
    editor.AddText("{\n")
    for entrance in solutions:
        editor.AddText("    Entrance " + str(entrance) + ": Length "
		+ str(solutions[entrance][0]) + "; "
		+ str(solutions[entrance][1]) + "\n")
    editor.AddText("}\n")
\end{verbatim}

Here is an example run:

\begin{verbatim}
>> Pretty(SolveDirectedRandomMaze(Randomize(CreateRandMaze(20, 7), 5)))

{
    0: []
    1: [8, 2, 11, 5, 7]
    2: [6, 12, 19]
    3: [9, 18, 13, 10, -1]
    4: [19, 18, 15, -1]
    5: [12]
    6: [8, 10]
    7: [15, 12, 14, -1]
    8: [12, 14, 15, -1]
    9: [16, 15]
    10: [13, 14, 15]
    11: []
    12: [16, 13, 17]
    13: [16, 19]
    14: [15]
    15: []
    16: [19]
    17: [19, 18, -1]
    18: [19]
    19: []
}
{
    Entrance 1: Length 2; [1, 2, 19]
    Entrance 3: Length 2.5; ['Random (1.5):', 3, [9, 16, 19], [18, 19], [13, 19], [10, 13, 19]]
    Entrance 4: Length 14.6666666667; ['Random (13.6666666667):', 4, [19], [18, 19], None]
}
\end{verbatim}
If we take entrance 1, the shortest path is nonspecial - just:

\begin{verbatim}
1 -> 2 -> 19.
\end{verbatim}

If we take entrance 3, it is a little harder; there are four path choices right from the first node:
\begin{verbatim}

    3 -> 9 -> 16 -> 19
    3 -> 18 -> 19
    3 -> 13 -> 19
    3 -> 10 -> 13 -> 19
\end{verbatim}

The lengths of these four choices (in hops) are 3, 2, 2, and 3, with an average length of
2.5.

If we take entrance 4, things are a little more complicated because we might get sent to
to the dead end of node 15; possible paths are:
\begin{verbatim}
    4 -> 19
    4 -> 18 -> 19
    4 -> 15 -> dead end
\end{verbatim}
Our lengths are 1, 2, and MAX (which we have arbitrarily set to be two times the total maze
size which here equals 40). $(1 + 2 + 40) / 3 = 14.3333333333$

We could adjust our sensitivity to possible dead ends by making MAX bigger or smaller.
\end{comment}

\begin{comment}
\ans{connecting-cities}
The minimum spanning tree (MST) problem is as follows: given a
connected undirected graph on vertices $V$  and edges $E$ with
each edge having a weight, compute a minimum-weight subset of edges $E'$ such that $(V,E')$
is connected.  There are $O(|V| + |E|)$ algorithms for solving the MST problem.

The problem of connecting cities can be mapped into an MST problem
as follows:  we draw horizontal and vertical lines through
each city and identify the intersection points; add these as new
vertices. The edge set consists of line segments corresponding to
intersections. We now look for the MST in this graph.

Claim: The MST yields an optimum set of roads.

nontrivial argument of correctness? (is it correct?)
% Hai's notes:  http://www.google.com/url?sa=t&source=web&ct=res&cd=9&ved=0CD4QFjAI&url=http%3A%2F%2Fwww.eecs.northwestern.edu%2F~haizhou%2F357%2Flec5.pdf&\end{itemize}=AEmZS_LsGouVtgfmnOm2CQ&usg=AFQjCNHiSWJf2Fg_k66oCLXeRZHJaKZRhA&sig2=eCLA9CV0ydwGEz6fS1WwPg
\end{comment}

\ans{tsp-choice}
Consider a directed graph $G=(V,E)$, where the vertices correspond to the cities.
Each pair of cities is connected by an edge.

Every plan corresponds to a cycle in the graph and vice versa.  So, we
need to find a cycle which maximizes the ratio of profit for
all jobs on the cycle to the cost of performing the jobs on the cycle.

Let $\rho_{\mbox{\scriptsize max}}$ be the maximum ratio
achievable. We can find $\rho_{\mbox{\scriptsize max}}$ by guessing
a ratio $\rho$ and seeing whether it is too large or too small.

Let $\rho$ be any positive real number.  Give each edge $e = (i,j)$ a
weight of $\rho \cdot c(e) - p(j)$,
where $c(e)$ is the cost of taking edge $e$ and $p(j)$ is the profit
of visiting node $j$.

If the graph has a negative cycle with this weight function, we claim that
$\rho < \rho_{\mbox{\scriptsize max}}$.

Let $C$ be such a cycle.  Then we know that $\rho c(C) - p(C) < 0$, where we
have extended $c$ and $p$ to sequences of edges in the natural way.
Therefore for cycle $C$, we have $p(C)/c(C) > \rho$, i.e., $\rho < \rho_{\mbox{\scriptsize max}}$.

Conversely, if all the cycles in the graph have a positive weight, it must be
that $\rho > \rho_{\mbox{\scriptsize max}}$. Since if $\rho_{\mbox{\scriptsize max}} \leq \rho$, let $C$ be a cycle
whose profit-to-cost ratio is $\rho_{\mbox{\scriptsize max}}$. Then $p(C)/c(C) = \rho_{\mbox{\scriptsize max}} \leq \rho$
which implies $p(C) -\rho c(C) \leq 0$, contradicting the absence of
nonpositive weight cycles.

There is a straightforward algorithm for computing the presence of
negative weight cycles which runs in $O(|V|\cdot |E|)$ time. We can perform
binary search to find $\rho_{\mbox{\scriptsize max}}$ with $0$ as a lower bound and $\max_{e\in E} p(e)/c(e)$.
The search can be terminated when we have determined $\rho_{\mbox{\scriptsize max}}$ to a specified
tolerance of $\epsilon$.

Clearly, it is not advantageous to make any move unless the profit-to-cost
ratio is greater than one. We can bound the maximum possible profit-to-cost ratio by finding the edge that maximizes the ratio of profit of
visiting its destination  to the
cost of traversing the edge. Suppose this cost is $R$, then we need to perform the search
between $1.0$ and $R$ for the optimum ratio. In order to narrow down
the search to an interval of size $\epsilon$, we would need $(\log (R-1)
- \log(\epsilon))/\log 2$ steps. Since each step involves finding a
negative cycle, it can be done in $O(|V|\cdot|E|)$ time using the
Bellman-Ford algorithm.

\ans{road-network}
The straightforward solution would be to compute the shortest path
from $A$ to $B$ for each proposal.

Note that we cannot add all the proposals at once; otherwise, we may end up with a shortest path which  uses
multiple proposals.

Instead we use an all-pairs shortest path algorithm on the original graph
to get a matrix $S(u,v)$ of shortest path distances for each
pair of vertices.  Each proposal $p$ is a pair of cities
$x,y$.  The best we can do by using proposal $p$ is $\min\big(S(A,B), S(A,x) + A(y,B)\big)$.
This computation is constant time, so we can
evaluate all the proposals in time proportional to the number of
proposals after we have computed the shortest path for each pair.
All-pairs shortest path can be computed in  $O(|V|\cdot|E|\log |V|)$ time
 by multiple calls to Dijkstra's algorithm or in $O(|V|^3)$ time using the Floyd-Warshall algorithm.

\ans{stable-assignment}
This is a classical problem and is solved using a ``proposal algorithm''.

Each student who does not have an adviser ``proposes'' to the most-preferred
professor to whom he has not yet proposed.

Each professor then considers all the students who have proposed to him and tells the
one he most prefers, ``I accept you" and "no" to the rest.
The professor is then provisionally matched to a student.

In each subsequent round, each student who does not have an adviser proposes to one professor
to whom he has not yet proposed (regardless of whether the professor has already accepted
a student or not)
and the professor once again replies with one ``accept'' and rejects the rest.

This may mean that professors who have already accepted a student can ``trade-up'' and students who have already been accepted by a professor can be ``jilted''.

This algorithm has two key properties:
\begin{itemize}
\itemsep 1pt
\item It converges to a state where everyone is paired. Everyone gets accepted at some point. Once a professor accepts a student, he always has a student.  There cannot be a professor and a student both unpaired since the student must have proposed to that professor at some point (since a student will eventually propose to everyone, if necessary) and being unpaired, the professor would have accepted.
\item The pairings are stable. Let Riemann be a student and Gauss be a professor. Suppose they are each paired but not to each other.
Upon completion of the algorithm, it is not possible for both Riemann and Gauss
to prefer each other over their current pairings. If Riemann prefers Gauss to his current professor, he must have asked Gauss before he asked his current professor. If Gauss accepted Riemann's proposal, yet is not paired to Riemann at the end, he must have dumped him for someone he preferred more and therefore does not like Riemann more than his current student. If Gauss rejected his proposal, he was already paired with someone he preferred to Riemann.
\end{itemize}

\ans{arbitrage}
We define a weighted directed graph $G = (V,V \times V)$, where $V$ corresponds to
the set of commodities. The weight $w(e)$ of edge $ e = (u,v)$ is  the amount
of commodity $v$ we can buy with one unit of commodity $u$.

Observe that an arbitrage exists iff there is a cycle in $G$
whose edge weights multiply out to more than 1.

Create a new graph $G' = (V,E)$ with weight function $w'(e) = -\log w(e)$.
Since $\log ab = \log a + \log b$, there is a cycle in $G$ whose edge weights
multiply out to more than 1 iff there is a cycle in $G'$ whose edge weights
sum up to less than $\log 1 = 0$.

We know how to efficiently find negative weight cycles in weighted directed graphs,
e.g., using the Bellman-Ford algorithm which takes $O(|V| \cdot |E|)$
time and
can use this to compute the existence of an arbitrage.

\ans{bvn}
First, note that the number of packets at input $i$ is the sum
of the elements in row $i$ and the number of packets destined
through output $j$ is the sum of all the elements in column $j$.

Let the maximum row sum be $R$---then it will take at least $R$ cycles
to transfer the packets from an input corresponding to $R$.
Similarly, if $C$ is the maximum column sum, it will take
at least $C$ cycles to transfer the packets to an output corresponding to $C$,
i.e., $\beta = \max(R,C)$ is a lower bound on the number of cycles.

We claim $\beta$ is actually a tight bound. To do this, we first prove that
we can create a matrix $A^\ast \geq A$ such that every row and column of
$A^\ast$ sums up to $\beta$.

The proof is by construction---starting with $A$, find a row and a column
whose sums are less than $\beta$ and increment that element by $1$.
Each successive matrix is larger than its predecessor and the process
must converge to a matrix whose rows and columns all sum up to $\beta$.

Now, consider a bipartite hypergraph on vertices $\{(L,0),\ldots,(L,n-1),
(R,0),\ldots,(R,n-1)\}$, where we have $A^\ast[i,j]$ edges
between vertex $(L,i)$ and $(R,j)$. Since the row
and column sums are all $\beta$, it follows that the degrees of all vertices
is $\beta$.

This graph has a perfect matching---this follows from the theorem
that a $\beta$-regular bipartite graph has a perfect matching which in
turn follows from Birkhoff's characterization of bipartite graphs, namely a perfect matching exists iff
every subset of size $k$ has at least $k$ neighbors.

A perfect matching is a permutation from inputs to outputs---by choosing these
assignments and performing the corresponding transfer, we can
reduce the number of packets to transfer from $A^\ast$ by $n$ and the
resulting matrix has rows and columns summing to exactly $\beta-1$.  In
this way, we can construct a schedule which transfers all the packets in $A\ast$
in $\beta$ cycles.   Since $A^\ast \geq A$, this schedule will
also transfer all the packets in $A$ in $\beta$ cycles.

\ans{shannon-capacity}
If the transmitter and receiver decide on a restricted
set of pairs of symbols rather than just symbols, they can do better than
$1$ bit per symbol transmitted.

The insight is that a pair like $(A,C)$ and $(B,E)$ cannot be mistaken
for each other since $C$ and $E$ cannot conflict.

A formal way of finding the largest set of pairs of symbols
which cannot be mistaken for each other is to create
a conflict graph on the 25 pairs $\{(A,A),\ldots,(E,E)\}$---put
an edge between $(u_1,u_2)$ and $(v_1,v_2)$ iff
$(u_1,v_1) \in \mathbf{\Pi}$ and $(u_2,v_2) \in  \mathbf{\Pi}$.

Now, we want to find a maximum independent set in this graph---i.e.,
the largest subset of vertices, not two of which are connected by an edge.

There are a number of such sets of cardinality $5$---e.g.,
\[
S = \{(A,A),(B,C),(C,E),(D,B),(E,D)\} .
\]
Therefore
we can send $\log_2 5$ bits with every two symbols which
amounts to roughly 1.16 bits per symbol transmitted.

\ans{team-photo-2}
In Solution~\ref{team-photo-1}, we showed how to model the
problem using a DAG, with each vertex corresponding to a player.
Problem~\ref{team-photo-2} is asking for
a minimum cardinality set of vertex disjoint paths in this DAG such
that each vertex appears on some path.

This problem can be reduced to a flow problem: let $G = (V,E)$ be a DAG.
Construct a flow problem $F$ as follows: define
$G = (V',E')$ from $G = (V,E)$ by creating
a left vertex $v_l$ and a right vertex $v_r$ for each vertex $v \in V$.

Add a new source vertex $s$, add edges from
$s$ to each left vertex, and add a sink vertex $t$ with edges from each right
vertex to $t$.

Add edges $(v_l,v_r)$ for each $v\in V$.
For each edge $(u,v)\in E$, add an edge $(v_r, u_l)$.

Assign a lower bound and upper bound of $1$ for each edge of the form $(v_l, v_r)$;
all other edges have  a lower bound of $0$ and upper bound of $\infty$.

By construction, the minimum feasible flow for $F$ defines
a minimum cardinality set of vertex disjoint paths.

\ans{dancing-with-the-stars}
The problem can directly be mapped into the weighted bipartite
matching problem: bidders and celebrities constitute the left and right
vertices; an edge exists from $b$ to $c$ iff $b$ has offered money to dance with
$c$ and the weight of an edge is the amount offered for the dance.
It can be solved using specialized algorithms, network flows, or linear programming.

If the requirement that bidders and celebrities be distinct is dropped,
the problem becomes a weighted matching problem in a general graph which is still
solvable in polynomial time.

\ans{2-cnf}
Let $\phi$ be a CNF expression of $n$ variables $x_0,\ldots,x_{n-1}$ and
$m$ clauses in which each clause contains no more than two variables.

Assume without loss of generality that each clause in $\phi$ contains
exactly two distinct variables since singleton clauses
force the value of the corresponding variable for a satisfying assignment.

Construct the directed graph $G_\phi$ on $2n$ vertices indexed by $x_0,\ldots,x_{n-1},{x_0}',\ldots,{x_{n-1}}'$.
For each clause $l_i + l_j$, add an edge from ${l_i}'$ to ${l_j}$ and
${l_j}'$ to ${l_i}$, where ${{x_i}'}'$ is interpreted as $x_i$.

{\em Claim}: $\phi$ is satisfiable iff for each $i$, there does not exist a path from
$v_{x_i}$ to $v_{x_i}'$ and a path  from $v_{x_i}'$ to $v_{x_i}$.

{\em Proof}: If an edge exists from $v_{x_i}$ to $v_{x_j}$, it means that whenever $x_i$ is true,
then $x_j$ must be true in a satisfying assignment for $\phi$. Similar results
hold for vertices corresponding to complemented
variables. By the transitivity of
logical implication, a path in $G_\phi$ from $v_{l_i}$ to $v_{l_j}$ implies that
if $l_i$ is true in a satisfying assignment for $\phi$, then so must $l_j$.

Now, consider the SCCs of $G_\phi$.  If for some $i$, $v_{x_i}$ and $v_{x_i}'$ are both
in the same SCC, there cannot exist a satisfying assignment for $\phi$.
Conversely, if for no $i$, $v_{x_i}$ and $v_{x_i}'$ are both
in the same SCC, we will prove that a satisfying
assignment for $\phi$ exists by constructing it as follows:
observe that if some $v_{x_i}$ or $v_{x_i}'$ is set to true
in an SCC, then all the corresponding variables in that SCC are set to
true, as are all variables in the descendants of the SCC.

Start with any source SCC in the SCC DAG which contains
$v_{l_i}$ and does not have a path to $v_{{l_i}'}$ (such a vertex must exist; otherwise, $v_{l_i}$ and $v_{{l_i}'}$ would
be in the same SCC).
Set $l_i$ to true and update all implied assignments, including setting ${l_i}'$ to false.
Iteratively perform this computation until all the literals have been assigned.  We can always
pick a literal to assign before the assignment is complete since no $v_{l_i}$ and $v_{{l_i}'}$
are in the same SCC and we are only reducing the SCC DAG.

Each clause will be satisfied after this is completed since each clause is of the form $l_i + l_j$.
Assume WLOG that we assign $l_i$ first.  If it is assigned to true, the clause is satisfied; otherwise, when $l_i$ is assigned to false, we will set ${l_j}$ to true.

\ans{toe}
Let $\phi$ be a set of equality and inequality constraints on variables
$x_0,\ldots,x_{n-1}$.  Create an undirected graph $G_\phi$ on vertices $x_0,\ldots,x_{n-1}$;
for each equality $x_i = x_j$, add the edge $(x_i,x_j)$.

Now examine the connected components of $G_\phi$.
By the transitivity of equality, we can infer that $x_i = x_j$ for all
vertices $x_i$ and $x_j$ in a common SCC.

Therefore if for some inequality $x_p \neq x_q$, vertices
$x_p$ and $x_q$ lie in the same SCC, the set of constraints $\phi$ is not satisfied.

Conversely, let there be $k$ connected components $C_0,\ldots,C_{k-1}$.  Assign
the variables in $C_i$ to the value $i$. This satisfies all the equality constraints and since all the inequality constraints involve variables
from different SCCs, all inequality constraints are satisfied too.

\chapter{ Algorithms on Strings}

\ans{string-search}
There are several interesting algorithms for substring search that run
in linear-time  such as Knuth-Morris-Pratt, Boyer-Moore, and
Rabin-Karp algorithm.  However in practice, for most applications, substring
search runs faster than that. We have found Boyer-Moore algorithm to
be the fastest in our experience.

The Boyer-Moore algorithm works by trying to match characters of $S$
in $T$ at a certain offset in the reverse order (last character of
$S$ matched first). If we can match all the characters in $S$, then we
have found a match; otherwise, we stop at the first mismatch.  The key idea behind the Boyer-Moore algorithm is to be able to skip as
many offsets as possible when we are done matching characters at a
given offset. We do this by building two tables---good suffix shift table and a bad character shift table.

For a given character, the bad character shift table gives us the distance of the last
occurrence of that character in $S$ to the rightmost
string.  If the character does not occur in $S$, then the entry in the
table is of length  $S$.  Hence when we find a character in $T$ that
does not match for the current offset, we know how much we must move
forward so that this character can match for the first time.

The good suffix shift table is a little more complex. Conceptually, for a given
suffix $X$ of $S$, it tells us what is the shortest suffix $Y$ of $S$
that is longer than $X$ and has $X$ as suffix. In practice, what we
store is how far can we move safely, given that we have matched up to $length(X)$ characters but
did not match the next character.

\ans{unique-char-string-search}
The most na\"{i}ve way of finding whether a string $S$ is a substring of
another string $T$  would be to test character by character at every
offset in $T$, if we find a match for $A$. However this would take
$O(m \cdot n)$ time, where $m$ is the length of $A$ and $n$ is the length of $T$.
We can do better than that. If at a certain offset we match a
set  of characters in $A$ to that of $T$ but they do not match all
the characters in $A$  since $A$ has all unique characters,  the
characters in $T$ that matched $A$ will not match $A$ at any other
offset. Hence we can skip a few offsets.
This essentially means that for every character in $T$, we
compare it with a character in $A$ at most once.  This will lead to a
linear-time matching algorithm that runs in $O(n +m)$ time.

\ans{rotate-string}
This is a special case of applying a permutation with constant
additional storage (cf.~Problem~\ref{perm}) except that the permutation is a rotation. In the
case of rotations, we get cycles of the form $(c,i+c,2i+c,\ldots, (m \cdot i
 + c)\bmod n)$ for different values of $c$ from $1$ through  a number of cycles. So, essentially all other
cycles are a shifted version of the first cycle. For example, consider
the case where $n = 6$ and $i = 2$, we get $(1,3,5)$ and $(2,4,6)$.
Once we have identified the difference between the lowest and the second lowest element in any cycle, we know the number of cycles there are
and their starting points.

\ans{test-rotation}
The key idea here is that if string $A$ is a rotation of another
string $B$, then $A$ must be a substring of $B \cdot B$. For example, since
$arc$ is a rotation of $car$, it is a substring of $carcar$. Since
substring test can be done in linear-time using the Knuth-Morris-Pratt
algorithm, we can test for rotation in linear-time.

\ans{normalize}
We are not providing explicit solution to this problem here since
there are no algorithmic ideas involved. Most times when this
kind of a
question is asked, you need to keep a few things in mind:
\begin{itemize}
\item A single pass over the string is likely going to be faster.
\item You can build prefix tables to match \texttt{index.html} and \texttt{default.html}
  in advance to speed up the process.
\item You may not know if you need to add the protocol part or not
  until you have reached the end of the host part. Hence it may be a good idea to leave some
  space for adding \texttt{http://} at the beginning of the buffer.
\end{itemize}

\ans{longest-palindrome}
This problem can be reduced to finding the longest common subsequence
between the input string and its reverse. We have already shown how
this can be done efficiently in Problem~\ref{longest-nondecreasing}.

\ans{pretty-printing}
This can be efficiently solved by dynamic programming. Let $C(a)$ be
the minimum wasted space for arranging the last $a$ words.  If we have
all the values for $C(i)$ tabulated for $i < a$, we can compute $C(a)$
by finding the number of words we can fit in the first line that
minimizes $C(a)$.

\ans{min-edit-distance}
This is another interesting application of dynamic programming.

Let $S(i,j)$ represent the substring of string $S$ that contains all
the characters of $S$ from index $i$ to $j-1$ (inclusive).  Let the edit
distance between the two strings $A$ and $B$ be represented by
$E(A,B)$. Let's say that $a$ and $b$ are, respectively,
the length of strings $A$ and $B$. We now make two claims:
\begin{itemize}
\itemsep 1pt
\item If $A[a-1]  = B[b-1]$ (i.e., the last two characters of the
  strings match), then $E(A,B) = E(A(0,a-1), B(0,b-1))$. This is
  obviously true since any set of transformation that turns $A$ into
  $B$  can turn $A(0,a-1)$ into $B(0,b-1)$ and vice versa.
\item If $A[a-1]  \not= B[b-1]$ (i.e., the last two characters of the
  strings do not match), then
\ifthenelse{\boolean{createspace}}{
\[ E(A,B) = \min\Big(E\big(A(0,a-1), B\big), E\big(A,
  B(0,b-1)\big)\Big) + 1 .\]
}{
  \begin{eqnarray*}
  \lefteqn{E(A,B) =} \\
  & & \min\Big(E\big(A(0,a-1), B\big), \\
  & &  \; \; E\big(A, B(0,b-1)\big)\Big) + 1 .
  \end{eqnarray*}
}
  We can see this to be true by observing that if
  there is a smaller sequence of events that leads to the transformation of
  $A$ into $B$, there must be a step where the last character of the
  string becomes the same as the last character of $B$.  This
  could happen either by inserting a new character at the end or
  deleting the last character. We can reorder the sequence such that
  this operation happens at the end. The length of the sequence would remain the same and we would still end up with $B$ in the end. In case this operation was ``delete'',
  then by deleting this operation, we get a sequence of operations
  that turn $A(0,a-1)$ into $B$. If this operation was an ``insert'', then
  by dropping this operation, we would have a set of transformations
  that turn $A$ into $B(0,b-1)$.  In either case, it would be a
  contradiction if there was a sequence of operations that turned $A$
  into $B$ which is smaller than $\min\Big(E\big(A(0,a-1), B\big), E\big(A,
  B(0,b-1)\big)\Big) + 1$.
\end{itemize}

We can use the above results to tabulate the values of $E\big(A(0,k),
B(0,l)\big)$ for all values of $k < a$ and $l < b$ in $O(a\cdot b)$ time till
we get the value of $E(A,B)$.

\ans{grep}
The key to solving this problem is using recursion effectively.

If the regular expression \texttt{r} starts with \texttt{\^},
then \texttt{s} must  match the remainder of \texttt{r};
otherwise, \texttt{s} must match \texttt{r} at some position.

Call the function that  checks whether a string \texttt{S} matches
\texttt{r} from the beginning \texttt{matchHere}.
This function has to check several cases---(1.)~length-0
regular expressions which match everything, (2.)~a regular
expression starting with a \texttt{*} match, (3.)~the regular expression
\texttt{\$}, and (4.)~a regular
expression starting with an alphanumeric character or dot.

Of these, (1.)~and (3.)~are base cases, (4.)~is a check followed
by a call to \texttt{matchHere}, and (3.)~requires
a new \texttt{matchStar} function.

The \texttt{matchStar} function does a walk down the string,
checking that the prefix thus far matches the alphanumeric character or dot until some suffix matches the remainder of the regular expression.

\lstinputlisting[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]{RegExp.java}

\chapter{ Intractability}

\ans{01knapsack}
The 0-1 knapsack problem is an NP-complete problem. However
the dynamic programming solution to the problem runs in
pseudopolynomial time (to be precise, its time complexity is $O(n\cdot W)$).

Let $A(w)$ be the maximum value that can be packed with weight less than or equal to $w$.
We can use the recurrence
\[ A(w) = \max\big(A(w-1), \max_i(A(w-w_i) + v_i)\big) .\]
For $w \leq 0$, we set $A[w] =0$.
Computing $A[w]$ given $A[i]$, for all $i < w$, takes $O(n)$ time; therefore this DP procedure computes $A[W]$ in $O(n\cdot W)$ time.

\ans{tsp}
A good way to approach this problem is to think of a related problem
that can be solved exactly efficiently. The minimum spanning tree (MST) problem
has an efficient algorithm and it yields a way of visiting each city exactly
twice---start at any city $c$ and perform an in-order walk
in the MST with $c$ as the root.  This traversal leads to a path in
which each edge is visited exactly twice.

Now consider any tour for a salesman---if we drop the final edge back
to the starting city, the remaining set of edges constitute a tree.
Therefore the cost of any traveling salesman problem is at least as great as the cost of the MST.

Now we make use of the fact that the distances between cities satisfies
the triangle inequality to build a tour from the MST whose
cost is no greater than the MST. When we perform our in-order walk,
we simply skip over cities we have already visited---the direct distance from
$u$ to $v$ cannot be more than the sum of distances on a path from $u$ to $v$.

Hence we have a tour costing at most twice the cost of the MST which itself
was an upper bound on the cost of the traveling salesman problem.

\ans{facility-location}
A natural approach to this problem is to build the assignment
one warehouse at a time. We can pick the first warehouse to be the city
for which the cost is minimized---this takes $\Theta(n^2)$ time since we
try each city one at a time and check its distance to every other city.

Let's say we have selected the first $i-1$ warehouses $\{c_1,c_2,\ldots,c_{i-1}\}$
and are trying to
choose the $i$-th warehouse. A reasonable choice for $c_i$
is the one that is the farthest from the $i-1$ warehouses already chosen.
This can also be computed in $O(n^2$) time.

Let the maximum distance from any remaining cities to a warehouse be $d_{m}$.
Then the cost of this assignment is $d_{m}$. Let $e$ be
a city that has this distance to the warehouses.
In addition, the $m$ warehouse cities are all at least $d_{m}$ apart; otherwise, we would have chosen $e$ and not $c_m$ at the $m$-th selection.

At least two of these $m+1$ cities have
to have the same closest warehouse in an optimum assignment.
Let $p,q$ be two such cities and $w$ be the warehouse they are
closest to. Since $d(p,q) \leq d(w,p) + d(w,q)$, it follows
that one of $d(w,p)$ or $d(w,q)$ is not less than $d_m/2$.
Hence the cost of this optimum assignment is at least $d_m/2$, so
our greedy heuristic produced an assignment that is within a factor of
two of the optimum cost assignment.
% We selected $c_{m-1}$ to be the point that was farthest from the first $m-1$
% warehouses.

Note that the initial selection of a warehouse is immaterial for the
argument to work but heuristically, it is better to choose
a central city as a starting point.

\ans{computing-exponents}
It is natural to try and solve this problem by divide-and-conquer, e.g.,
determine the minimum number of multiplications for each of $x^{k}$ and $x^{30/k}$,
for different values of $k$.  The problem is that the subproblems are not
independent---we cannot just add the minimum number of multiplications for computing
$x^{5}$ and $x^{6}$ since both may use $x^{3}$.

Instead we resort to branch-and-bound: we maintain a set of partial solutions
which we try to extend to the final solution. The key to efficiency is
pruning out partial solutions efficiently.

In our context, a partial solution is a list of exponents that we have
already computed. Note that in a minimum solution, we will never have
an element repeated in the list. In addition, it suffices to consider
partial solutions in which the exponents occur in increasing order
since if $k > j$ and $x^k$ occurs before $x^j$ in the chain, then
$x^k$ could not be used in the derivation of $x^j$. Hence we lose nothing by advancing the
position of $x^k$.

Here is code that solves the problem:

\lstinputlisting[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]{MinExp.java}

The code runs in a fraction of a second.
It reports  $\langle x^{1}, x^{2}, x^{3}, x^{5}, x^{10}, x^{15}, x^{30}\rangle$.
In all, 7387 partial solutions are examined.

There are other potential bounding techniques: for example, from the binary
representation of 30 ($11110$), we know that $7$ multiplications suffice (computing
$x^2, x^4,x^8,x^{16}$ and then multiplying these together).
In addition, we could keep out duplicate partial solutions.
The code could avoid considering all pairs $i,j$ and focus on pairs that just involve
the last element since other pairs will have been considered previously.
More sophisticated bounding can be applied:
a chain like $\langle x, x^2, x^3,x^6,x^7\rangle$ will require at least three
more multiplications (since $\lceil \frac{30}{7}\rceil = 3$) and so this chain
can be safely pruned.
When selecting a partial solution to continue searching from, we could
choose one that is promising, e.g., the shortest solution---this might lead to
better solutions faster and therefore more bounding on other search paths.

For hand calculations, these techniques are important but
they are trickier to code and our original code solves the
given problem reasonably quickly.

\ans{cnf-sat}
A reasonable way to proceed is to use branch-and-bound: we choose a variable
$v$, see if there is a satisfying assignment when $v=0$ and if not,
we try $v=1$. If there is no satisfying assignment for $v=0$ and
for $v=1$, the expression in not satisfiable.

Once we choose a variable and set its value, the expression simplifies---we
need to remove clauses where $v$ appears
if we set $v=1$ and remove clauses where $v'$ appears when we set $v=0$.
In addition, if we get to a unit clause---one where a single literal appears,
we know that in any satisfying assignment for
the current expression, that literal must be set to true; this rule
leads to additional simplification.  Conversely, if all the clauses are true,
we do not need to proceed further---every assignment to the remaining variables
makes the expression true.

There are various choices for selecting variables. One natural choice
is to pick the variable which appears the most times in clauses with
two literals since it leads to the most unit clauses on simplification.
Another choice is to pick the variable which is the most binate---i.e., it appears
the most times in negated and nonnegated forms.

\ans{scheduling-classes}
We are given a set of $N$ unit duration lectures and $M$ classrooms. The lectures can be held simultaneously as long as no two lectures need to happen in the same classroom at the same time and all the
precedence constraints are met.

The problem of scheduling
these lectures  so as to minimize the time taken to completion is known
to be NP-complete.

This problem is naturally modeled using graphs.  We model
lectures as vertices, with an edge from vertex $u$ to vertex $v$ if
$u$ is a prerequisite for $v$.  Clearly, the graph must be acyclic for
the precedence constraints to be satisfied.

If there is just one lecture room, we can simply hold the lectures
in topological order and complete the $N$ lectures in $N$ time (assuming
each lecture is of unit duration).

We can develop heuristics by observing the following: at any time, there
is a set of lectures whose precedence constraints have been satisfied.
If this set is smaller than $M$, we can schedule all of them;
otherwise, we need to select a subset to schedule.

The subset selection can be based on several metrics:
\begin{itemize}
\itemsep 1pt
\item Rank order lectures based on the length of the longest
dependency chain that they are at the start of.
\item Rank order lectures based on the number of lectures
that they are immediate prerequisites for.
\item Rank order lectures based on the total number of lectures
that they are direct or indirect prerequisites for.
\end{itemize}
We can also use combinations of these criteria to order the lectures
that are currently schedulable.

For example, for each vertex, we define its criticality to be the length of a longest path
from it to a sink.  We schedule lectures by processing vertices
in topological order.  At any point in our algorithm, we have a set of candidate
lectures---these are the lectures whose prerequisites have already been scheduled.

If the candidate set is less than size $M$, we schedule all the lectures; otherwise, we choose the $M$ most critical lectures and schedule those---the
idea is that they should be scheduled sooner since they are at the start of
longer dependency chains.

The criterion is heuristic and may not lead
to optimum schedules---this is to be expected since the problem is NP-complete.
Other heuristics may be employed, e.g., we may use the number of lectures that depend
on lecture $L$ as the criticality of lecture $L$ or some combination of the criterion.

\ans{Hardy-Ramanujan}
This problem is very similar to another very popular problem that
is asked in interviews. You
are given an $n \times n$ matrix in which both rows and columns are sorted in ascending order and you are supposed to find a given
number in the matrix.

In this case, we are essentially looking for an
implicit matrix $A$ such that $A(i,j) = i^3 + j^3$.  In our case, the
matrix will have $n^{1/3}$ rows and columns. There are several
algorithms for searching  for a number in such a matrix that are linear
in the number of rows.

One approach is to start by comparing $x$ to $A_{n,1}$. If $x = A_{n,1}$,
stop. Otherwise, there are two cases:
\begin{itemize}
\itemsep 1pt
\item $x > A_{n,1}$, in which case $x$  is greater than all elements in Column~1.
\item $x < A_{n,1}$, in which case $x $ is less than all elements in Row~$n$.
\end{itemize}
In either case, we have a matrix with $n$ fewer elements to search.
In each iteration, we remove a row or a column, which means we inspect $2n-1$ elements.

%TODO: hacked for better break
\newpage
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
bool IsSumOfcubes(int n) {
  int m = ceil(pow(n, 1/3));
  int i = m; int j = 0;
  while( j < m && i >= 0) {
    int k = i*i*i +j*j*j;
    if (k == n) {
      return true;
    } else if (k < n) {
      ++j;
    } else {
      --i;
    }
  }
}
\end{lstlisting}

For a tight lower bound, let $x$ be any input. Define $A$ to be:
\[
\left[
\begin{array}{lllll}
 & & & & x-1 \\
 & & & & x+1 \\
 & & & \cdots &     \\
 & &x-1 & &     \\
 & &x+1 & &     \\
 &x-1 & & &     \\
 &x+1 & & &     \\
x -1 & & & &
\end{array}
\right]
\] where all entries not shown are 0.
We claim that any algorithm that solves the matrix search problem will have to compare
$x$ with each of the $2n-1$ elements
shown (i.e., the diagonal elements and the elements immediately below them).
Call these elements the $\Delta$ elements.

Comparing $x$ with other elements does not eliminate any of the $\Delta$ elements.
Suppose an algorithm did not compare $x$ with one of the $\Delta$ elements.
Then we could make that element $x$ (instead of $x-1$ or $x+1$) and the algorithm
would behave exactly as before and hence return the wrong result. Therefore
at least $2n-1$ compares are necessary which means that the algorithm we designed is optimum.

Note that for this problem, if the input number is $n$, the size of
the input is $\log n$ bits. Since the runtime is $O(n^{1/3})$, it is
still an exponential algorithm in the size of the input.

\ans{collatz}
Often interview questions are open-ended
with no definite good solution---all you can do is provide some good
heuristics and code it well. For the Collatz hypothesis,
the general idea is to start with each number and iterate
till you reach one. Here are some of the ideas that you can try to accelerate
the check:
\begin{enumerate}
\itemsep 1pt

\item Reuse computation by storing all the numbers you have already
  proven to converge  to 1; that way, as soon as you reach such a number, you can
  assume it would reach 1.
\item In order to save hash table space, you can keep only odd numbers
  in the hash table.
\item   If you have tested every number up to $k$, you can stop
  the chain as soon as you reach a number that is less than or equal to
  $k$. Also, you do not need to store the numbers below $k$ in the
  hash table, so you can keep deleting these numbers from the hash table as
  you progress.
\item If multiplication and division are expensive, use bit shifting
  and addition.
\item Since the numbers in a sequence may grow beyond 32 bits, you should
  use 64 bit longs and keep testing for overflow.
  % (probability of this happening should be fairly low).
\end{enumerate}

\ans{closest-pair-2}
The brute-force solution is to consider all pairs of points: this yields
an $O(n^2)$ algorithm.

A reasonable approach is to split the points into two equal-sized sets using a line $x = P$ parallel to the Y-axis.  Such a line
can be found by computing the median of the values for the
$x$ co-ordinates---this calculation can be performed
using randomization in a manner analogous to Quicksort.

We can then compute the closest pair of points recursively
on the two sets; let the closest pair of points on the left of
$P$ be $d_l$ apart and the closest pair of points
to the right of $P$ be $d_r$ apart.  Let $d = \min(d_l,d_r)$.

Now, all we need to look at is points which are in the band $[P-d, P+d]$.
In degenerate situations, all points may be within this band. So, if we
compare all the pairs, the complexity becomes quadratic again.
However we can sort the points in the band on their $y$ co-ordinates
and scan the sorted list, looking for points $d$ or less distance
from the point being processed.

Intuitively, there cannot be a large
number of such points since otherwise, the closest pair
in the left and right partitions would have to be less than $d$ apart. This intuition
can be analytically justified---Shamos and Hoey's famous
1975 paper ``Closest-point problems'' shows that no more than 6 points
can be within $d$ distance of any point which leads to an $O(n \log n)$ algorithm---the time
is dominated by the need to sort.

The recursion can be sped up by switching to brute-force when
a small number of points remain.
%---the quadratic
%complexity of the brute-force approach is offset by the reduced overhead of recursion.

\ans{prime} Here are a couple of simple heuristics that you can use to
speed up primality tests:
\begin{enumerate}
\item It is sufficient to test for factorization up to $\lceil \sqrt n \, \rceil$.
\item You can limit yourself to prime numbers only. You may not know
  all the prime numbers between $2$ and $\sqrt n$, however you can use
  the fact that all prime numbers other than 2 and 3 are of the
  form $6k +1$ or $6k -1$. This would speed up your computation by a
  factor of $3$.
\end{enumerate}

\chapter{ Parallel Computing}

\ans{servlet-with-caching}
The na\"{i}ve solution would be:
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]
public class S1 implements Servlet {
  String wLast = null;
  String [] closestToLastWord = null;

  public void service(ServletRequest req, ServletResponse resp) {
    String w = extractWordToCheckFromRequest(req);
    if (checkWord.equals(wLast)) {
      encodeIntoResponse(resp, closestToLastWord);
    } else {
      wLast = w;
      closestToLastWord = closestInDictionary(w);
    }
  }
}
\end{lstlisting}

This solution has a race condition---Thread~A might have
written \texttt{wLast} and then Thread~B reads \texttt{wLast} and \texttt{closestToLastWord}
before Thread~A has a chance to update \texttt{closestToLastWord}.
The call to \texttt{closestToLastWord} could take quite long or be very fast, depending
on the length of \texttt{checkWord}.  Hence it is quite possible that
between the two write operations of Thread~A, Thread~B reads both \texttt{wLast} and \texttt{closestToLastWord}.

A thread-safe solution would be to declare \texttt{service} to be synchronized;
in this case, only one thread could be executing the method
and there is no race between write to \texttt{wLast} and \texttt{closestToLastWord}.
This leads to poor performance---only one servlet thread can be executing at a time.

The solution is to lock just the part of the code that operates
on the cached values---specifically, the check on the cached value
and the updates to the cached values:
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]
public class S2 implements Servlet {
  String wLast = null;
  String [] closestToLastWord = null;

  public void service(ServletRequest req, ServletResponse resp) {
    String w = extractFromRequest(req);
    String result  = null;
    synchronized (this) {
      if (w.equals(wLast)) {
        result = closestToLastWord.clone();
      }
    }
    if (closestToLastWord == null) {
      result = closestInDictionary(i);
      synchronized (this) {
        closestInDictionary = result;
        wLast = w;
      }
    }
    encodeIntoResponse(resp, result);
  }
}
\end{lstlisting}

In the above code, multiple servlets can be in their call to \texttt{closestInDictionary}
which is good because the call may take a  long time.
Because we lock on \texttt{this}, the read-assignment on a hit
and write-write assignment on completion are atomic. Note that
we have to \texttt{clone} \texttt{closestToLastWord} when assigning
to \texttt{result} since otherwise, \texttt{closestToLastWord} might change before we encode
it into the response.

\ans{thread-pools}
The first attempt to solve this problem might be to have \texttt{main}
launch a new thread per request rather than process the request
itself:
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]
class ThreadPerTaskWebServer {
  public static void main(String [] args) throws IOException {
   final ServerSocket socket = new ServerSocket(80);
    while ( true ) {
      final Socket connection = socket.accept();
      Runnable task = new Runnable() {
        public void run() {
          handleRequest(connection);
        }
      }
      new Thread(task).start();
    }
  }
}
\end{lstlisting}

The problem with this approach is that we do not control the
number of threads launched. A thread consumes a nontrivial
amount of resources by itself---there is the overhead of
starting and ending down the thread and the resources
used by the thread. For a lightly-loaded server,
this may not be an issue but under load, it can result in
exceptions that are challenging, if not impossible, to handle.

The right trade-off is to use a {\em thread pool}. As the
name implies, this is a collection of threads, the size of which
is bounded. Java provides thread pools through the \texttt{Executor}
framework.

\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]
class TaskExecutionWebServer {
  private static final int NTHREADS = 100;
  private static final Executor exec
      = Executors.newFixedThreadPool(NTHREADS);

  public static void main(String[] args) throws IOException {
    ServerSocket socket = new ServerSocket(80);
    while (true) {
      final Socket connection = socket.accept();
      Runnable task = new Runnable() {
        public void run() {
          handleRequest(connection);
        }
      };
      exec.execute(task);
    }
  }
}
\end{lstlisting}

\ans{async-replies}
Our strategy is to launch a thread $T$ per \texttt{Requestor} object.
Thread $T$ in turn launches another thread, $S$, which calls
\texttt{execute} and \texttt{ProcessResponse}.
The call to \texttt{execute} in $S$
is wrapped in a try-catch \texttt{InterruptedException} loop;
if \texttt{execute} completes successfully,
\texttt{ProcessResponse} is called on the result.

After launching $S$, $T$ sleeps for the timeout interval---when
it wakes up, it interrupts $S$. If $S$ has completed, nothing happens; otherwise, the try-catch \texttt{InterruptedException} calls \texttt{error}.

Code for this is given below:
\lstinputlisting[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]{AsyncThread.java}

\begin{comment}
Send requests, register call-backs.  Responses are asynchronous.
Need to demultiplex messages---unique identifier.
Can have fault-tolerance in the form of time-outs, does not appreciably add
to the overhead.
Need a single thread to manage the demuxing.
\end{comment}

\ans{timer}
There are two aspects to the design---first, the data-structures and second, the locking mechanism.

One solution is to use two data-structures.
The first is a heap in which we insert key-value pairs: the keys are runtimes and the values are the thread to run at that time.
A dispatch thread runs these threads; it sleeps from call
to call and may be woken up if a thread is added to or deleted
from the pool.  If woken up, it advances or retards its remaining
sleep time based on the top of the heap.
On waking up, it looks for the thread at the top of the
heap---if its launch time is the current time, the dispatch thread
deletes it from the heap and executes it. It then sleeps till
the launch time for the next thread in the heap. (Because of
deletions, it may happen that the dispatch thread wakes up and finds
nothing to do.)

The second data-structure is a hash table with thread ids as keys
and entries in the heap as values. If we need to cancel a thread,
we go to the heap and delete it.  Each time a thread is added,
we insert it into the heap; if the insertion
is to the top of the heap, we interrupt the dispatch thread
so that it can adjust its wake up time.

Since the heap is shared by the update methods and the dispatch thread,
we need to lock it. The simplest solution is to have a single lock
that is used for all read and writes into the heap and the hash table.

\begin{comment}
\ans{synchronization}
We can solve this by using two locks, $L$ and $M$.
We use $L$ to guard access to the number $n$ of outstanding tasks;
when a new task is launched it increments $n$ and
when it completes, it decrements $n$ and waits on $M$ before
exiting.

When a thread decrements $n$ to $0$, it calls \texttt{notifyAll}
on $M$---this wakes up all the threads waiting on $M$
who can now exit.

\end{comment}

\ans{rw-1}
We want to be able to indicate whether the string is being read
as well as whether the string is being written to.  We achieve this
with a pair of locks---LR and LW and a read counter locked by LR.

A reader proceeds as follows: it locks LR, increments the counter, and
releases LR.  After it performs its reads, it locks LR, decrements the counter, and releases LR.
A writer locks LW, then iteratively performs the following: it locks LR, checks to see if the read counter is 0;
if so, it performs its write, releases LW, and then releases LR.
In code:
\lstinputlisting[basicstyle=\footnotesize,numbers=left,breaklines=true]{RW.java}

\ans{rw-2}
We want to give writers the preference.  We achieve this by
modifying the solution above to have a reader start by locking LW and
then immediately releasing LW.  In this way, a writer who acquires the
LW lock is guaranteed to be ahead of the subsequent readers.

\ans{rw-3}
We can achieve fairness between readers and writers by having
a bit which indicates whether a read or a write was the
last operation performed.  If the last operation performed was a read,
a reader on acquiring a lock must release the lock and retry---this gives
writers priority in acquiring the lock; a similar operation is performed
by writers.

\ans{producer-consumer}
This problem can be solved for a single producer and a single consumer with
a pair of semaphores---{\em fillCount} is incremented and {\em emptyCount} is
decremented whenever an item is added to the buffer.  If the producer
wants to decrement {\em emptyCount} when its count is 0, the producer
sleeps.  The next time an item is consumed, {\em emptyCount} is incremented
and the producer is woken up.  The consumer operates analogously.
The Java methods, \texttt{wait} and \texttt{notify}, can
be used to implement the desired functionality.

If there are multiple producers and consumers, the solution above has two
races---two producers
can try writing to the same slot and two consumers can read from
the same slot.  These races can be removed by adding mutexes around the
{\em putItemIntoBuffer} and {\em removeItemFromBuffer} calls.

\ans{barber}
A casual implementation is susceptible to races. For example,
a new customer sees the barber cutting hair and goes to the waiting room;
before he gets to the chair, the barber completes the haircut,
checks the waiting room, and goes back to his chair to sleep.
This is a form of livelock---the barber and the customer are both idle,
waiting for each other.
As another example, in the absence of appropriate locking,
two customers may arrive simultaneously, see the barber
cutting hair, and a single vacant seat in the waiting room, and
go to the waiting room to occupy the single chair.

One way to achieve correct operation
is to have a single mutex which allows only
one person to change state at a time.
The barber must acquire the mutex before checking
for customers; he must release it when he either begins to
sleep or begins to cut hair. A customer must acquire
the mutex before entering the shop; he must release it when he sits in either
a waiting room chair or the barber chair.

For a complete solution, in addition to the mutex, we need event semaphores
to record the number of customers in
the waiting room and the number of people getting their hair cut.
The event semaphore recording the number of customers in the waiting room
is used to wake up the barber when a customer enters; the event
semaphore recording the number of customers getting a haircut
is used to wake up waiting customers.

\ans{dining-philosophers}
The natural solution is for each resource to have a lock.
The problem arises when each thread $i$ requests lock $i$ and
then $i+1 \bmod{n}$.  Since all locks have already been acquired,
the thread deadlocks.

One approach is to have a central controller, which
knows exactly which resources are in use and arbitrates conflicting requests.
If resources are not available for a thread,
the controller can reject his request.

Another solution is to order the resources and require that resources be
acquired in increasing order and released in decreasing order.
For example, if all threads request simultaneously, resource $n-1$
will be left unrequested (since Thread $n-1$ will request
$0$ first, and then $n-1$).  Thread $n-2$ will then succeed at
acquiring resource $n-1$ since Thread $n-1$ will block on Resource $0$.

\begin{comment}
The example below shows a solution where the chopsticks are not represented explicitly.
Philosophers can eat if none of their neighbors are eating.
This is comparable to a system where philosophers that cannot get the second chopstick must put down the first chopstick before they try again.

In the absence of locks associated with the resources, threads must ensure
that the decision to enter $m$ is not based on stale information about the
state of its neighbors. If Thread 2 sees that Thread 1 does not
hold resource 1, then Thread 2 looks at Thread 3, Thread 1 could take resource 1
while Thread 2 looks at Thread 3. This problem can be avoided
by using a single mutual exclusion lock not associated with the resources but
with the decision procedures that can change the states of the philosophers.
This is ensured by the monitor. The procedures test, pickup, and putdown are
local to the monitor and share a mutual exclusion lock. Notice that threads
calling \texttt{WAITC(x)} release the lock and wait on the variable \texttt{x}
until another thread calls \texttt{SIGNALC(x)} on the same variable.
When the process that called \texttt{WAITC} resumes, it will have reacquired the lock.
\end{comment}

This solution is not starvation-free, e.g.,
T2 can wait forever while T1 and T3 alternate.
To guarantee that no thread starves, one could keep track of the
number of times a thread cannot execute when his neighbors release their locks.
If this number exceeds some limit, the state of the thread
could change to \texttt{starving} and the decision procedure to enter the critical
section could be supplemented to require that none of the neighbors are starving.
A philosopher that cannot pick up locks because a neighbor is starving
is effectively waiting for the neighbor's neighbor to finish eating.
This additional dependency reduces concurrency---raising the threshold for
transition to the \texttt{starving} state reduces this effect.

\chapter{ Design Problems}
% \dp{A note about solutions}
Many of the problems in this chapter can be the basis for PhD-level research.
A comprehensive discussion on the solutions available for such problems is
outside the scope of this book.  In an interview setting when someone asks such
a question, you should have a discussion in which
you demonstrate an ability to think creatively, understand design
tradeoffs, and attack unfamiliar problems. The
answers in this chapter are presented in this context---they
are meant to be examples of good responses in an
interview and are not definitive state-of-the-art solutions.

\ans{mosaic}
As mentioned in the prologue to this book, one approach
is to do a coarse pixelization of the tiles and for each
potential tile position, find the tile in the image that is closest to
it in terms of a norm defined over each pixel color. If the image
collection is limited, you would often end up with significant
errors. Since the human eye perceives the average color of a region, it
has been observed that if you adjust the average target color of a
tile based on errors made by its neighboring tiles, it improves the
overall quality.

Often the target image may have very similar color for a large number
of tiles in the background. If we pick the same image over and over
for a contiguous region, it stands out in the mosaic and does not create
very good aesthetics. Hence the mosaic tools would usually allow the
users to specify constraints on how often a tile can be repeated or
a minimum separation between the two copies of an image.

Given a rectangle in the target image, finding the best image that
can approximate it essentially boils down to searching for the nearest
neighbor in some $k$-dimensional space (where $k$ is the number of
color pixels used to approximate the image). Since we can do some preprocessing on the library of images, it makes sense to do some
spatial indexing.  A very simple indexing scheme for relatively low
value of $k$ would be to just form a $k$-dimensional grid and place
the images to the closest point on the grid. A more sophisticated
approach would be to use $R$-trees for indexing.
%TODO: senthil suggested mentioned the rectangle packing problem here.
%Amit: Not sure if I Can do this here. Rectangle packing would mean different %sizes of tiles. Not sure how to do packing and closest point in k-dimensional %space optimization simultaneously.

Finding the overall best fit under the constraints of how often an
image can be repeated is NP-hard. However greedy approaches work
reasonably well.

\ans{search-engine}
The predominant way of doing this is to build inverted indices. In an inverted index, for each word, we store a list of locations where the word
occurs. Here location is defined to be the pair of document id and the
offset in the document.  The list is stored in sorted order of
locations (first ordered by document id, then by offset). When we are
looking for the documents that contain a set of words, what we need to
do is find the intersection of lists for each word. Since the lists
are already sorted, the intersection can be done in linear-time (linear in
the total size of the lists). There are various optimizations that
can be done to this basic infrastructure. We list a few thoughts below.
\begin {itemize}
\itemsep 1pt
\item \textrm{Compression}---compressing the inverted index
  helps both with the ability to index more documents as well as memory
  locality (fewer cache misses). Since we are storing sorted lists,
  one way of compressing is to use delta compression where we only
  store the difference between the successive entries. The deltas can be
  represented in fewer bits.
\item \textrm{Caching}---the distribution queries is often fairly skewed
  and it helps a great deal to cache the results of some of the most
  frequent queries.
\item \textrm{Frequency-based optimization}---since search results often
  do not need to return every document that matches (only top ten or
  so), only a fraction of highest quality documents can be used to
  answer most of the queries. This means that we can make two
  inverted indices, one with the high quality documents that stays in the
  memory and one with the remaining documents that stays on the disk. This
  way if we can keep the number of queries that require the secondary
   index to a small enough number, then we can still maintain a reasonable throughput
  and latency.
\item \textrm{Intersection order}---since the total intersection time
  depends on the total size of lists, it would make sense to intersect the words with smaller sets first. For example, if we are looking for
  ``USA GDP 2009'', it would make sense to intersect the lists for GDP and 2009
  before trying to intersect the list for USA.
\end{itemize}
We could also build a multilevel index
to improve accuracy on documents. For high priority web pages, we can
recursively from ``document'' abstraction introduce a notion of ``paragraph''
and then ``sentence'' to index further down. That way
the intersections for the words might be within the
same context. We can pick results with closer
index values from these lists.

\ans{ip-lookup}
This is a well studied problem because of its implications for
building a high speed Internet backbone. There are a number of
approaches that have been proposed and used in IP routers.
One simple approach is to build a trie data-structure such
that we can traverse the trie for an IP address till we hit a node that has a
label. This essentially requires one pointer indirection per bit of input. The lookup speed
can be improved a little at the cost of memory by making fatter nodes
in the trie that consume multiple bits at a time.

There are a number of approaches that have been tried in software and
hardware to speed the lookup process:
\begin{itemize}
%TODO:(amit) feedback from senthil below:
%One way of reducing this is to do a binary... In order for binary search
% to work... terminate early. The paragraph
%needs more elaboration.
\itemsep 1pt

\item Binary search on hash tables---we can have one hash table
  for each possible length of prefix and then do a search for the
  longest matching prefix by looking through all the hash tables. However this could take 32 hash table lookups. One way of
  reducing this is to do a binary search for the longest matching
  prefix. In order for binary search to work, we would have to insert
  additional prefixes in the hash tables to ensure that if a longer prefix exists,
  binary search does not terminate early. This can be done by performing a binary search for each prefix and insert additional dummy entries wherever the binary search terminates early. This could inflate the size
  of hash tables by at most $\log_2 32$ (in practice, it is much
  smaller).

\item Ternary Content Addressable Memory (TCAM)---a TCAM is a special
  piece of hardware, where instead of storing 0s and 1s, a single unit
  of memory can also store a third state called the ``don't care''
  state. Also, the contents of memory can be addressed by partial
  contents of the memory. TCAMs with 32 address bits are used to store
  prefixes. Each prefix is padded with ``don't care'' bits to make it 32
  bits. This way, when we use an IP address to address the TCAM, we
  get all the matching prefixes. A priority logic gate then selects the
  longest matching prefix.
\end{itemize}

\ans{spell-check}
The basic idea behind most spelling correction systems is that the misspelled word's edit distance from the intended word tends to be very small
(one or two edits). Hence if we keep a hash table for all the words in the dictionary and
look for all the words that are within two edit distances of the text, most
likely, the intended word will be found in this set.  If the alphabet
has $m$ characters and the search text
has $n$ characters, we would need to perform roughly $n\cdot m^2$ hash table lookups. When we intersect all the strings within two edit
distance with the dictionary words, sometimes we can land up with a
fairly large set of words and it is important to provide a ranked list
of suggestions to the users such that the most likely candidates are at
the top. This is often done by various probabilistic
models. There are various interesting ideas that can be used to
improve the spelling correction system:
\begin{itemize}
\itemsep 1pt
\item \textrm{Typing errors model}---often spelling mistakes are a result
  of typing errors. Typing errors are easy to model based on keyboard
  layouts.
\item\textrm{Phonetic modeling}---a big class of spelling errors happen
  when the person spelling it knows how the words sounds but does not
  know the exact spelling. In such cases, it helps to map the text to
   phonemes and then find all the words that map to the same phonetic
  sequence.
\item\textrm{History of refinements}---often users themselves provide a
  great amount of data about the most likely misspellings by first
  entering a misspelled word and then correcting it.
  This kind of historic data is often immensely valuable for spelling
  correction.
\item\textrm{Stemming}---often the size of dictionary can be reduced by only
keeping the stemmed version of the words in it and stemming the query
text as well.
\end{itemize}

\ans{stemming}
%TODO(amit): feedback from senthil
% Solution 9.5: This thought occurred to me:
% for words beginning with a letter of the alphabet, build a trie of a smaller
% depth, coalesce anything beyond this depth into a single node.
% Compute edit distances for all pairs of words at this
% node. The ones that have deletions in the end can be used together,
% this is again quite approximate. We can play with
% depths for each letter of the alphabet to distribute them better.
Stemming is a fairly large topic and different systems have adopted
different approaches. Porter stemmer developed by Martin Porter is
considered one of the most authoritative algorithms for stemming in
the English language. Here we mention some basic ideas related to
stemming, however this is in no way a comprehensive discussion on
stemming approaches.

The basic idea in most stemming systems works based on some simple rewrite rules, such as, if the word ends with ``es'' or ``s'' or ``ation'', then we remove
them.  Sometimes, a simple termination may not work, for example,
$\mbox{wolves} \mapsto \mbox{wolf}$. In order to cover this case, we may have a
rule to replace a suffix ``ves'' to ``f''.  In the end, most rules amount to matching a set of
suffixes and depending upon which one we end up with, we may apply a
certain transformation to the string. One way of efficiently doing this
could be to build a finite state machine based on all the rules.

A more sophisticated system might have several exceptions to the broad
rule based on the stem matching some patterns. For example, the Porter
stemmer defines several rules based on a pattern of vowels and
consonants.

Other approaches include use of stochastic method to learn rewrite
rules and N-gram based approaches where we look at the surrounding
words to determine the correct stemming for a word.

\ans{throttle}
This problem as posed, has some ambiguity:
\begin{itemize}
\itemsep 1pt
\item Since we usually download one file in one request, if a file is
  greater than $b$ bytes, there is no way we can meet the constraint
  of serving fewer than $b$ bytes every minute, unless we  can work with
  the lower layers of networking stack such as the transport layer or the
  network layer. Often the system
  designer could look at the distribution of file sizes and conclude
  that this problem happens so infrequently that we do not care.
  Alternately, we may choose to serve no more than the first $b$ bytes of any file.

\item Given that the host's bandwidth is a resource for which there could be
contention, one important design choice to be made is how to resolve a contention. Do we let requests get served in first-come first-served order or is there a notion of priority? Often
crawlers have a built-in notion of priority based on how important the
document is to the users or how fresh the current copy is.
\end{itemize}

One way of doing this could be to maintain a server with which each
crawler checks to see if it is okay to hit a particular host. The server
can keep an account of how many bytes have been downloaded from the
server in the last minute and not permit any crawler to hit the server if
we are already close to the quota. If we do not care about priority,
then we can keep the interface synchronous where a server requests for
permission to download a file and it immediately gets approved
or denied. If we care about priorities, then the server may enqueue the
request and inform the crawler when it is alright to download the
file. The queues at the permission server may be based on priorities.

In case the single permission server becomes a bottleneck for the
system, we can use multiple servers such that the responsibility of a
given host is decided by hashing the host name and assigning it to a
particular server based on the hash range.

\ans{page-rank}
Since the web graph can have billions of nodes and it is mostly a
sparse graph, it is best to represent the graph as an adjacency list.
Building the adjacency list representation of the graph itself may
require significant amount of computation, depending upon how the
information is collected. Usually, the graph is constructed by
downloading the pages on the web and extracting the hyperlink
information from the pages. Since the URL of a page can be arbitrarily
long and varies a lot in size, it is often a good idea to represent
the URL by a hash value.

The most expensive part of PageRank algorithm is the repeated matrix
multiplication. Usually, it is not possible to keep the entire graph
information in a single machine's RAM. There are usually two
approaches to solving this problem:
\begin{itemize}
\itemsep 1pt
\item Disk-based sorting---in this approach, we keep the column
  vector $X$ in memory and load each row at a time.  For a given row
  $A_i$, we write out pairs of numbers $\langle j, A_{ij}\cdot
  X_j\rangle$ to disk. Then we sort the pairs by their first component
  on disk and then add up the second component to get the result
  vector.  The advantage of this approach is that as long as we can
  hold the column vector in the RAM, we can do the entire computation
  on a single machine. However this approach can be fairly slow
  because disk-based sorting is usually slow.

\item Partitioned graph---in this approach, we use $n$ machines
  and partition the vertices (web pages) into $n$ sets. Usually, the
  partitioning is done by partitioning the hash space such that it is
  easy to determine which vertex maps to which machine. Given this
  partitioning, each machine loads its vertices and their outgoing
  edges into RAM. Each machine also loads the parts of the PageRank
  vector that corresponds to its vertices. Then each machine does a
  local matrix multiplication. Since some of the edges on each machine
  would correspond to the nodes that are owned by other machines, the
  result vector is going to contain nonzero entries for vertices that
  are not owned by the local machine. So, at the end of local
  multiplication, we need to send updates to other hosts so that
  these values can be correctly added up. The advantage of this
  approach is that we can process arbitrarily large graphs as long as
  we have sufficient number of machines.
\end{itemize}

\ans{priority-system}
If we have sufficient RAM on a single machine, the most simple
solution would be to maintain a min-heap where we maintain
all the events by their priority.  Since we are interested in a
scalable solution to this problem, we need to partition the problem
across multiple machines.

One way of doing this could be to hash the
events and partition them into ranges so that one hash range
corresponds to one machine. This way, the insert and delete operations can
be done by just communicating with one of the servers. However in
order to do the extract-min operation, we need to send a find-min
message to all the machines, infer the min from all their
responses, and then try to delete it.

Since at a given time, all the clients would be interested in the same
event (the highest priority event), it is hard to distribute this
problem well.
If a large number of clients are
trying to do this operation at the same time, we may run into a
situation where most clients will find that the absolute min event they were
trying to extract has already been deleted. If the throughput of this
service can be handled by a single machine, we can keep one server
that is responsible for responding to all the machines. This
server can prefetch top hundred or so events from each of the machines
and keep them in a heap.

In many applications, we do not need strong consistency guarantees. What we need is that overall, we spend most of our resources taking care of the highest priority jobs. In such cases, a client can pick one of the hash ranges randomly
and just request the highest priority job from the corresponding
machine. This would work great for distributed crawler application but
it would be a bad idea for event driven simulation.

\ans{min-latency}
Often clients of a service care more about the $99$-th or the $95$-th
percentile latency for the server rather than the mean latency since they want
most of the requests to be serviced in a reasonable amount of time even if an
occasional request takes very long. If our architecture is such that
at a time only a fixed number of requests can get served and other
pending requests must wait for a slot to open up before getting
served, it is important to design our queuing system in such a way
that the requests that take a very long time to serve do not block many small
jobs behind them.

Consider the case where the time it takes for the server to process a
request is a function of the request. Given the
distribution of requests, the service time follows a Pareto distribution. In
such cases, it greatly helps to have two queues and pick a good
threshold such that the requests that take longer than the threshold
time, go to one queue and the requests that take less than or equal to
the threshold time, go to the other queue. We pick the threshold such that
the majority of jobs go to the faster queue and the jobs in this queue
are never blocked behind a big job.  The larger jobs do have to wait
more behind the larger jobs but overall this strategy can greatly
reduce the $99$-th percentile latency.

Often the system designer does not know how long a given request is
going to take in advance in order to make the right queuing decision. It has
been shown that even in such cases, it is advantageous to keep two
queues. When a request comes in, it is put in the fast queue, however
when it takes longer than a certain threshold time, we cancel the
request and put it at the back of the slow queue.

\ans{adwords}
Reasonable goals for such a system could include:
\itemsep 1pt
\begin{itemize}
\item providing users with the most relevant ads
\item provide advertisers with the best possible return on their investment
\item minimizing the cost of running such an operation
\end{itemize}

There are two key components to building such a system:
(1.)~the front-facing component, by which advertisers can add
their advertisements, control when their ads get displayed, how much
and how they want to spend their advertising money, and review
the performance of their ads and (2.)~the ad-serving system
which selects which ads to show on the searches.

The front-facing system can be a fairly conventional website design.
Users interact with the system using a browser and opening a
connection to the website. You will need to build a number of features:
\begin{itemize}
\itemsep 1pt
\item \textrm{User authentication}---a way for users to create accounts and
  authenticate themselves.
\item\textrm{User state}---a set of forms to let advertisers specify things
  like their advertising materials, their advertising budget etc. Also
  a way to store this information persistently.
\item\textrm{Performance reports}---a way to generate reports on how and
  where the advertiser's money is being spent.
\item\textrm{Human interactions}---even the best of automated systems require
  occasional human interaction and a way to interfere with the
  algorithms. This may require an interface for advertisers to be able
  to contact customer service representatives and an interface for
  those representatives to interact with the system.
\end{itemize}
\begin{comment}
There are standard techniques
for maintaining a list of users, and authenticating them.

Users can enter their ads using text-boxes or file uploads (if
ads can be in image or flash format). They can also enter what
keywords, locales, times-of-day, etc. they want the ads to be shown
in.  The entry can be made more efficient using AJAX, e.g.,
auto-completing keywords, and suggesting synonyms.

Users also need to specify how much they want to spend in a day,
how much they are willing to pay for an ad to be displayed,
and how much they will pay for an ad that is clicked on.
\end{comment}

The whole front-end system can be built using, for example, HTML and
JavaScript, with a LAMP stack (Linux as the operating system, Apache as the HTTP
server, MySQL as the database software, and PHP for the application logic)
responding to the user input.

\begin{comment}
In addition to the frontend seen by the users, it would
also be necessary to have a customer service frontend, by which
customer service representatives (CSRs) can over-ride default limits
on the number of ads or keywords, compute detailed comparisons
of ads, bill customers, etc.

The system also needs to be able to look for
ads that potentially violate policies on copyright violation or use abusive language.  CSRs can be alerted on ads
being posted that refer to known copyrights or use words from a dictionary
of potentially unacceptable words.
\end{comment}

The ad-serving system would probably be a less conventional web service.
It needs to choose
ads based on their ``relevance'' to the search,
perhaps some knowledge of the user's search history, and how much the advertiser is
willing to pay. A number of strategies could be envisioned here for
estimating relevance, such
as, using information retrieval or  machine learning techniques that learn from past user interactions.

%TODO(amit): added from senthil's mail, pls check
%amit: either I am not understanding  or this seems like a complex way of solving a simpler matching problem.
% there is another class of ad serving problems where different ads define %different constraints about what can get served and what cannot and the concern % is to meet ad reservations. There flow makes a lot of sense. I Can write about % this at some point.
%One approach to the ad-serving problem could be to use network flow. There are %$m$ ads on the source side and $n$ web pages on the sink side. An ad has an %edge to a page if it is relevant to it. The costs of the ads are
%the capacities of edges from the source. Page frequency ratio times the total %sum of ad costs will be the capacities on the edges to the sink. A max flow
%might give us an optimal pairing. Relevance can be found with textual
%analysis with ad tags and data mining of the web page.

The ads can be added to the search results
by embedding JavaScript in the results that pulls in the ads from the
ad-serving system directly. This helps isolate the latency of serving search results from the latency of serving ad results.

\ans{rec-system}
% It is natural to provide snippets from ``similar'' pages, with each snippet being
% a hyperlink.
The key technical challenge in this problem is to come up with the list of
articles---the HTML code for adding these to a sidebar is trivial.

One suggestion might be to add articles that have proven to be popular recently.
Another is to have links to recent news articles.  A human reader at Jingle
could tag articles which he believes to be significant. He could
also add tags such as finance, politics, etc. to the articles.
% and an article to a finance article could be linked only to those which have tags.
These tags could also come from the HTML meta-tags or the page title.

We could also sometimes provide articles at random and see how popular
they prove to be; the popular articles can then be shown more
frequently.

On a more sophisticated level, Jingle could use automatic textual analysis,
where a similarity is defined between pairs of articles---this similarity is
a real number and measures how many words are common to the two. Several
issues come up, such as the fact that frequently occurring words such
as ``for'' and ``the'' should be ignored and that having
rare words such as ``arbitrage'' and ``induction'' in common is more important
than having say, ``America'' and ``international''.

Textual analysis has problems, such as the fact that two words may
have the same spelling but completely different meanings (anti-virus
means different things in the context of articles on AIDS and computer security).

One way to augment textual analysis is to use collaborative filtering---using
information gleaned from many users. For example, by examining cookies and
time-stamps in the web server's log files, we can tell what articles individual users have read.
If we see many users have read both $A$ and $B$ in a single session, we might
want to recommend $B$ to anyone reading $A$. For collaborative filtering to work,
we need to have a substantial number of users.

% Textual analysis, currentness, clustering based on log file analysis, rules, breaking articles,
% user knowledge, tags; collaborative filtering

\ans{poker}
An online poker playing service would have a front-end system
which users interact with and a back-end system which runs
the games, manages money, looks for fraud, etc.

The front-end system would entail a UI for account management---this would cover
first-time registration, logging-in, managing online persona, and
sending or receiving money.
In addition, there would be the game playing UI---this could be
as simple as some HTML rendering of the state of the game (cards in
hand, cards on the table, bets) and a form to enter a bet.
A more sophisticated UI might use JavaScript to animate cards being
dealt, change the expression on player's images, status messages or smileys from
players, etc.

The back-end needs to be able to form tables of players,
shuffle in a truly random manner,
deal correctly, check if the player's moves are legal, and update
player's finances.  It can be implemented using, for example, a Java servlet
engine which receives HTTP requests, sends appropriate responses, and updates
the database appropriately.

One of the big challenges in such a system is fault-tolerance.
On the server side, there are standard techniques for this, such
as replication.

% http://www.onlinepokerfaq.com/guide/cheating.html
On the client side, there is the possibility that
a player may realize he is in a poor situation and claim that his Internet
connection went down. This can be resolved by having a rule that
the server will bid on the player's behalf if the player does not respond
quickly enough. Another possibility is having the server treat the
player who is disconnected as being in the game but not requiring any
more betting of him.  This clearly can be abused by the player, so
the server needs to record how often a player's connection hangs
in a way that is favorable to him.

Collusion between players is another serious problem.  Again, the server logs
can be mined for examples of players working together to share knowledge
of their cards or squeeze other players out. In addition, players
can themselves flag suspicious play and customer service representatives can investigate further.

Random number generation is an intensely studied problem but is
still easy to get wrong. A fairly frequent problem is using process id
as a seed for a random number generator, which means that
there are only roughly 20,000 possible sequences of random numbers.
This means that, on an average, knowing the first 4 cards is enough
to predict the order of the rest of the cards
since $ \log_2 \binom{52}{4} \approx 18.04 > \log_2 20000 = \approx 14.28$.

\ans{driving-directions}
At its core, a driving directions service needs to store the map as a
Graph, where each intersection and street address is a vertex and the
roads connecting them are edges. When a user enters a starting
address and an ending address, it finds the corresponding vertices and
finds the shortest path connecting the two vertices (for some definition of shortest). There are several issues that come up:
\begin{itemize}
\itemsep 1pt

\item Address normalization---a given address may be expressed by
  the user in different ways, for example, ``street'' may be shortened
  to ``st'', there may not be a city and state, just zip code or vice
  versa. We need a way to normalize the addresses to a standard
  format. Sometimes an underspecified address may need to be mapped to
  some concrete address, for example, a city name to the city center.
\item Definition of shortest---different users may have different
  preferences for routing, for example, shortest distance or fastest
  path (considering average speed on the road), avoiding use of freeways,
  etc. Each of these preferences can be captured by some notion of
  edge length.
\item Approximate shortest distance---given the enormity of a
  graph representing all the roads in a large country, it would be
  fairly difficult for a single server to compute the shortest path
  using standard shortest path algorithms and return in a reasonable
  amount of time. However using the knowledge that most long paths go
  through a standard system of highways and the fact that the nodes
  and edges in the graph represent points in euclidean space, we can
  devise some clever approximation algorithms that run much
  faster.
\end{itemize}

\ans{isbn-lru}
To quickly lookup an ISBN number, we would want a hash table
data-structure. However it would take $O(n)$ time to find the least-recently-used item in a hash table to discard. One way to improve the
performance would be to be lazy about garbage collection such that the
cost of removal of least-recently-used ISBNs can be amortized over
several lookups. To be concrete, let's say we want the cache to be of
size $n$, then we do not delete any entries from the hash table till it
grows to the size of $2n$. At this point, we go over the entire
hash table, looking at the number of times this item was used,
find the median number of times a value in the hash table was used, and then
discard everything below the median. This way, the cost of delete
operations is $O(n)$ but it will happen at least at the interval of $n$
lookups, hence the amortized cost of deletion is $O(1)$ at the cost of
doubling the memory consumption.

\ans{file-copy}
Assume that the bandwidth from the lab machine is a limiting factor.
It is reasonable to first perform trivial optimizations, such as
combining the articles into a single file and compressing this file.

Opening 1000 connections each five minutes from the lab server to
the 1000 machines in the datacenter and transferring the
latest news articles is not feasible since the total data transferred
will be approximately 5~terabytes (without compression) every five minutes.

Since the bandwidth between machines in a datacenter is very high,
we can copy the file from the lab machine to a single machine
in the datacenter and have the machines in the datacenter
complete the copy.  Instead of having just one machine serve
the file to the remaining 999 machines, we can have each machine
that has received the file initiate copies to the machines that
have not yet received the file.  In theory, this leads to an exponential
reduction in the time taken to perform the copy.

There are several issues which have to be dealt with: should a machine initiate
further copies before it has received the entire file? (This is tricky
because of link or server failures.) How should the knowledge of machines
which do not yet have copies of the file be shared? (There can be a
central repository or servers can simply check others by random
selection.)  If the bandwidth between machines in a datacenter is not a
constant, how should the selections be made? (Servers close to each other, e.g., in the
same rack, should prefer communicating with each other.)

Finally, it should be mentioned that there are open source
solutions to this problem, e.g., Unison, which would be a good place to start.

\ans{leader-election}
Think of the hosts as being vertices in a directed graph with an
edge from $A$ to $B$, if $A$ initially know $B$'s IP address.

We will study variants of this problem---synchronized or unsynchronized
hosts and known or unknown bounds on the network; compare
them with respect to convergence time, message size, and the number
of messages. We assume the graph is strongly connected (otherwise, the problem is unsolvable).

First, assume that the hosts are all synchronized to a common
clock (there are standard protocols which can allow computers
to synchronize within a few tens of milliseconds; alternately,
GPS signals can be used to achieve even tighter synchronization).

We will consider the case where the number of hosts $N$ and
the diameter $D$ of the network is known to all the hosts.
Our algorithm will elect the host with the highest IP address as the
leader.
The simplest algorithm for leader election is flooding---each
host keeps track of the highest IP address it
knows about; the highest IP address is initialized to
its own IP address.

Since hosts are synchronized, we can proceed in rounds.
In each round, host propagates the highest IP address
it knows of to each of its (initial) neighbors. After
$D$ rounds, if the highest IP address a host knows of is
its own, it declares itself the leader.

There is a small improvement to this algorithm which reduces
the number of messages sent---a host sends out an update
only when the highest IP address it knows about changes.

It takes $D$ rounds to converge and $|E|\cdot D$ messages are
communicated.
The number of iterations to convergence can be reduced
to $\log_2 D$ by having each host send the set
of hosts it has discovered in each iteration to each
host it knows about. This leads to faster convergence
since the distance to the frontier of undiscovered hosts
doubles in each iteration. However it requires much
more communication---the final round involves $N$
hosts sending $N$ messages and each message
has the ids of $N$ hosts. Furthermore, unlike the
original algorithm, this variant requires messages
to potentially traverse longer routes (in the original
algorithm, a host communicated only with the hosts it knew about initially).
The algorithm works correctly even if $D$ is just an upper bound on the true diameter.

When $N$ and $D$ are completely unknown, leader election can be performed through a distributed BFS.
Each host starts by sending out a search message to all
of its outgoing neighbors. In any round, if a host
receives a search message, it chooses one of the
hosts from which it received a search as its parent and
informs its parent about its selection.
(Since we are assuming an IP network, a child
can directly communicate its selection back
to its parent.)

This procedure constructs a BFS tree for each host.
Completion can be detected by having hosts respond
to search messages with both a parent or nonparent message
as well as a notification of completion from its children.
When BFS completes, each host has complete knowledge
of the graph and can determine the leader.

Now, we consider the asynchronous case. The flooding
algorithms we considered earlier cannot be directly
generalized to asynchronous hosts because there
is no notion of a round.  However we can simulate
rounds by having hosts tag their messages with
the round number. A host waits to receive
all round $r$ messages from all its neighbors before
performing its round $r$ update.

Note that this algorithm cannot avoid sending messages if
the highest IP it knows about does not change in round $r$
since the neighbors depend on receiving all their round $r$
messages before they can advance.

\ans{discovery}
Discovery and leader election are identical, so the solution to
Problem~\ref{leader-election} works here too.

\chapter{ Discrete Mathematics}

\ans{binom}
It is tempting to try and pair up terms in the numerator and denominator for the
expression for  $\binom{n}{k}$ that have common factors and try to somehow  cancel them out.
This approach is unsatisfactory because of the need to have factorizations.

The binomial coefficients satisfy several identities, the most basic of which is the {\em addition
formula:}
\[
 \binom{n}{k} =  \binom{n-1}{k} +  \binom{n-1}{k-1} .
\]
There are various proofs of this identity, ranging from the combinatorial interpretation
to induction and finally, direct manipulation of the expressions.

This identity yields a straightforward recursion for $\binom{n}{k}$.
The base cases are  $\binom{r}{r}$ and  $\binom{r}{0}$, both of which are 1.
The individual results from
the subcalls are integers and if  $\binom{n}{k}$ can be represented by an \texttt{int}, they can too; so, overflow is not a concern.

The recursion can lead to repeated subcalls and consequently exponential runtimes. There is
an easy fix---cache intermediate results as in dynamic programming. There are
$O(n^2)$ subproblems and the results can be combined in $O(1)$ time, yielding an $O(n^2)$ complexity bound.

\ans{climbing-stairs}
\begin{comment}
% AA's solution:
Let $S(k)$ denote the number of ways of climbing a
staircase with $k$ steps.
Note that $S(0) = 1$ and $S(1) = 1$.
There are two choices for the final step---either take two steps or one step; both
yield different jump sequences.
Therefore $S$ satisfies the following recurrence:
\begin{eqnarray*}
S(k) & = & S(k-1) + S(k-2), \;\;\mbox{if} \; k > 1.
\end{eqnarray*}
The solution to this is the Fibonacci sequence.
\end{comment}
% AP's solution:
Let $F(n)$ be the number of ways of climbing $n$ stairs through a
combination of one or two steps.  We note that $F(1) = 1$ and $F(0) =
1$.
Now, all paths that lead us to cross $n$ steps either start with a
single step or a double step. In case of a single step, there are
$F(n-1)$ ways of completing the path. In case of a double step, there
are $F(n-2)$ ways of completing the path. Hence
\[ F(n) = F(n-1) + F(n-2) . \]
This leads to a simple dynamic programming algorithm that can compute
$F(n)$ in $O(n)$ time. An interesting thing to note
here is that $F(n)$ has the same recurrence relationship as the Fibonacci
numbers and $F(n)$ is actually the $(n +1)$-th Fibonacci number.

\ans{ramsey}
This problem can be modeled using undirected graphs where vertices correspond to guests.
There is a pair of edges between every pair of guests.  Color an edge between a pair of guest ``blue''
if they are friends, otherwise, color it ``red''.

Then the theorem is equivalent to the claim
that in any clique on six vertices, where the edges are either blue or red, there is a subset of
three vertices, all connected by edges of the same color.

Choose any vertex $v$.  Examine the five edges with an endpoint
in $V_0$.  There must be at least three edges which are of the same color $c$
(this follows from the pigeon-hole principle).  Let $(v,\alpha), (v,\beta), (v,\gamma)$ be three such edges. Now, either there is an edge colored $c$
between one of $\alpha,\beta$ and $\gamma$, in which case $v$ and the vertices in $\alpha,\beta$ and $\gamma$
are connected by edges colored $c$ or there is no such edge, in which case $\alpha,\beta$ and $\gamma$ are
themselves connected by edges that are of the same color.

\begin{comment}
\ans{red-black}
For any search tree, the worst-case lookup time is dictated by its height.  We need to know the maximum
height a tree storing $n$ keys can have.  It is an elementary fact that there must be $n-1$
internal nodes in a search tree with $n$ keys.

We will now compute a bound on the height $h$
as a function of the number of internal nodes.
Let $H(n)$ be this quantity.
We claim that $H(n)$ is at most $2 \log(n+1)$.

We prove this by induction on $n$: it trivially holds for $n=0$ since the tree must consist of a single leaf and therefore have height $0$.

For the inductive step, consider a tree with $n+1$ internal nodes.  The root must be an internal node with
two children.

Note---seems to require the use of the Red-Black property, CLRS has a proof (page 274, Ed.~2).
Unless we stick to just proving an $O(\log n)$ bound.
\end{comment}

\ans{500-doors}
Number the doors from 1 to 500.
Let's start with some examples---door 12 is toggled on days 1, 2, 3, 4, 6, 12; door 3 is toggled
on days 1 and 3; door 1 is toggled on day 1; door 500 is toggled on days 1, 2, 4, 5, 10, 20, 25, 50, 100, 125, 250, 500.

The pattern that emerges is the following: a door is toggled as many times as its id has divisors.
Divisors come in pairs: $12 = 1\times 12 = 2\times 6 = 3\times 4$. So, the total number of divisors is even, except when the number is a perfect square.
For the perfect square case, the total number of divisors is odd.
Therefore the doors that are open at the end of the process are those with ids
1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324,
361, 400, 441, 484---these are 22 doors altogether. In the general case, it
would be $\lfloor\sqrt{n}\rfloor$, where $n$ is the number of doors.

%Source: AA---maybe Zeitz?

\ans{height-determination}
Let $F(k,l)$ be the maximum number of floors that can be tested by
$k$ identical balls and at most $l$ drops. We know that $F(1,l) =
l$. If we are given an additional ball to drop, we can drop the first
ball at $F(k,l-1) $ floor. If it breaks, then we can use the
remaining balls and $l-1$ drops to determine the floor exactly; if it
does not break, then we could drop the first ball at $F(k,l-1)  +
F(k, l-2)$ floor. If it breaks, we can use the remaining $k$ balls
and $l-2$ drops to narrow down the exact floor between $F(k,l-1)  +
1$ and $F(k,l-1)  +
F(k-1, l-2)$.  Continuing this argument till $k-1$ drops of the first
ball, we can test up to $\sum_{i=1}^{l-1} F(k, l-i)$  floors. Hence

\[F(k+1,l) = \sum_{i=0}^{l-1} F(k, l-i) . \]

Given the above recurrence relationship it is straightforward to
observe that $F(k+1,l)   = \binom{ k + l  -1}{k}$ since it follows
exactly the same recurrence relationship. (One easy way to notice this is to
tabulate some concrete values for $F(k,l)$.)  Now, since $F(k,l)$ monotonically
increases in $k$ and $l$, we can easily invert it to determine the number of
drops needed, given the number of balls and the number of drops.

\ans{card-color-bet}
A good way to begin this problem is to come up with some strategy
that guarantees a positive return. It is possible to guarantee a $2\times$
return by
waiting till the last card and betting the entire amount
on the last card whose color is uniquely determined by the
the 51 cards that have already been seen.

To do better than a $2\times$ return, consider the case of a deck of
$4$ cards with $2$ red cards and $2$ black cards.  If we do not bet
on the first card, there will be three remaining cards. Assume, without loss of generality, that two cards are black and one is red. If we bet \$$\frac{1}{3}$ on the next card being black and are successful,
then we have \$$\frac{4}{3}$ which we can double on the last card for
a $\frac{8}{3} > 2$ return. If we lose, then the two remaining cards are black,
in which case we can double our remaining money twice, i.e., achieve
a $\frac{2}{3} \times 2 \times 2 = \frac{8}{3} > 2$ return.
Note that this analysis assumes we can bet arbitrary fractions of the money
we possess.

Now, we consider the case where we can only bet in penny increments.
Let $Q(c,r,t)$ be the most we can guarantee, when we have $c$ cents to
gamble with and there are $r$ red cards remaining out of a total of $t$ cards.
We can bet $b$ cents, $0 \leq b \leq c$ on the next card.
Since we have to design a strategy that maximizes the worst-case
payoff, the maximum amount we can make on betting on red cards is given by
\ifthenelse{\boolean{createspace}}{
\begin{eqnarray*}
Q_R(c,r,t) & = & \max_{b\in \{0,1,2,\ldots,c\}} \min\big( Q(c+b, r-1, t-1), Q(c-b,r,t)\big).
\end{eqnarray*}
} {
\begin{eqnarray*}
\lefteqn{Q_R(c,r,t)  = } \\
& & \max_{0 \leq b \leq c } \min\big( Q(c+b, r-1, t-1), Q(c-b,r,t)\big).
\end{eqnarray*}
}
The maximum we can make by betting on black cards is
\ifthenelse{\boolean{createspace}}{
\begin{eqnarray*}
Q_B(c,r,t) & = & \max_{b\in \{0,1,2,\ldots,c\}} \min\big( Q(c+b, r, t-1), Q(c-b,r-1,t)\big).
\end{eqnarray*}
}
{
\begin{eqnarray*}
\lefteqn{Q_B(c,r,t) =} \\
& & \max_{0 \leq b \leq c} \min\big( Q(c+b, r, t-1), Q(c-b,r-1,t)\big).
\end{eqnarray*}
}
Hence $Q(c,r,t) = \max\big(Q_R(c,r.t), Q_B(c,r,t)\big)$ which yields a dynamic programming
algorithm for computing the maximum payoff---base cases are of the form $Q(c,0,t)$
and $Q(c,t,t)$, both of which are $c\times 2^t$.

However if we directly try and compute $Q(100,26,52)$, the algorithm runs for an
unacceptably long time. This is because we will be exploring paths for which $c$ grows
very large. Since we are given the maximum payoff on a dollar
when fractional amounts can be bet is less than $9.09$, we can prune computations for $Q(c,r,t)$
when $c \geq 909$. The following code implements the dynamic programming algorithm
with this pruning; it computes the maximum payoff, $808$, in two minutes.

\lstinputlisting[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]{CardSelect.java}

\ans{odd-even}
The process will converge since at each step, we reduce the number of integers in $A$ by one.
The number of odd integers removed in
each step is even since we either remove two odd integers or none.
Therefore if there were an even number of odd integers to begin with,
the last integer must be even; if there were an odd number of odd numbers to begin with, it must be odd.

\ans{gassing-up}
Consider the thought experiment of starting at an arbitrary city with sufficiently large amount
of gas so that we can complete the loop. In this experiment, we note the amount
of gas in the tank as the vehicle goes through the loop at each
city before loading the gas kept in that city for the vehicle. If
we start at a different city with a different amount of gas, the amount
of gas in the tank at each city should still vary in the same fashion
with a constant offset. If we pick the city where the amount of gas in
the tank is minimum as the starting point, clearly we will never run out
of gas. This computation can be easily done in linear-time with a
single pass over all the cities.

\begin{comment}
Let the cities be numbered $0,1,\ldots,n-1$, and let it take $d_i$
gas to drive from city $i$ to $i+1 \bmod{n}$. Suppose there is $g_i$
gas available at city $i$.

We are given that $G = \sum_{i=0}^{n-1} g_i = D = \sum_{i=0}^{n-1}  d_i$.

Suppose for the sake of contradiction---there is no city from
which we can start with an empty tank and drive a complete
cycle.

Suppose we start at $0$, and the furthest we can get to without running
out of gas is $F_0$ ($F_0$ may be $0$ itself).
This means that the sum of the $g_i$'s from $0$ to $F_0$ (inclusive)
is strictly less than the sum of the $d_i$'s from $0$ to $F_0$.

Now consider starting at $F_0 + 1$, say the furthest we can get
to is $F_1$.  In this way, we get a series of paths
$\langle 0,\ldots, F_0\rangle$, $\langle F_0 + 1,\ldots, F_1\rangle$,
etc.

For a path $\langle A,\ldots, B\rangle$, we will refer to $A$ as
being the start, and $B$ as being the end.

Since the number of cities is finite, at some point we must have
the end $e$ of one path being the start $s$ of another path.  If we sum
up the $g_i$'s for all the cities occurring on the paths starting at $s$
until we get to $e$, we will get $k \cdot G$, where $k > 0$ (since we may loop
around multiple times).  Similarly, the sum of the $d_i$'s will be $k \cdot D$.

But, by construction, for each path, the sum of the $g_i$'s is less than the
sum of the $g_i$'s, which leads to a contradiction.

Here's a much simpler solution: there must be some city $i$ with enough gas $g_i$ to drive to
the next city. Logically, the gas from city $i+1 \bmod{n}$ can be added to the gas at
city $i$, and city $i+1 \bmod{n}$ can be removed from consideration. The remaining
cities, together with $i$ (which now has gas $g_i + g_{i+1 \bmod{n}}$) satisfy
the conditions of the problem, so we can inductively continue till we've got just one city with
enough gas to complete the circuit.
\end{comment}

\ans{suicidal}
Consider the case where exactly one person has green eyes. The
statement from the explorer would make it clear to the
person with green eyes that he  has green eyes since nobody else
that he sees has green eyes.

Now, suppose there are two inhabitants with green eyes.
The first day, each of these two inhabitants would see exactly one other person with
green eyes. Each would see the other person on the second day too, from which
they could infer that there must be two
inhabitants with green eyes, the second one being themselves.
Hence both of them would leave the second day.

Using induction, we can demonstrate that if there are $k$ inhabitants
with green eyes, all the green-eyed inhabitants would leave after the
$k$-th assembly. We already saw the base case, $k=1$. Suppose the
induction hypothesis  holds
for $k-1$.  If there are $k$ inhabitants with green
eyes, each inhabitant with green eyes would see $k-1$ other
inhabitants with green eyes.
If at the $k$-th assembly, they see that nobody has departed,
it would indicate that they themselves have green eyes and hence
all the green-eyed inhabitants would leave on the $k$-th day.

As for the second part of the question, for $k=1$, it is
fairly obvious that the explorer gave new knowledge to the person with
green eyes. For other cases, the new information is a bit subtle. For $k=2$, the green-eyed inhabitants would be able to infer
the color of their eyes on the second day based on the information that
everyone on the island knows that there are green-eyed inhabitants
and yet no one left. For $k=3$, they are able to infer
because everyone knows that everyone knows that there are green-eyed
inhabitants and yet on the second day no one left.

Suppose $x$ is some fact and $E(x)$ represents the fact that everyone knows $x$ to be true. In this case, let $g$ represent the fact
that there are some green-eyed inhabitants on the island. Then on the $k$-th day, all the green-eyed
inhabitants would use the fact $E^K(g)$ to infer that they have green-eyes.
Essentially, what the explorer did by announcing the fact in the assembly is that it became ``common knowledge'', i.e., $E^{\infty}(g)$ became true.

\begin{comment}
It is easiest to consider this problem when there are only two
inhabitants---then each of the two inhabitants knows there are two eye colors, and knows
the other inhabitant's eye color, and can can therefore immediately infer his own.

Now consider the case where there are three inhabitants. Two must have one
color, say brown, and the other must have blue eyes. The blue-eyed inhabitant realizes
that his eyes must be blue since he knows the others have brown eyes.
He will kill himself at midnight, and therefore, not appear the next day. The two brown-eyed
inhabitants can infer that their own eye color must be brown for the
blue-eyed inhabitant to have inferred his own color, and they will kill themselves.

For the case of four inhabitants, if there are three  with one color, and one with the other,
the latter will infer his color, and then the first three will know that they all had
the same color.  If there are two of each color, then all will be alive the
next day, and therefore each knows that his color cannot be the same as the two
who have a common color.  Proceeding inductively, if there are $2N$ inhabitants,
they will all kill themselves on the $N$-th day.

For the second part of the question, the new knowledge injected by
the explorer is subtle. For the case $N=3$ with one blue-eyed and two
brown-eyed inhabitants, the explorer is telling the inhabitants with blue-eyes
something new. For the case $N=4$,  with two inhabitants of each color, the explorer is telling
the inhabitants that the others know that there are two colors (which would
not be the case if 3 had one color since the remaining inhabitant could have
the same color).
\end{comment}

\ans{hershey}
If the assumption is that once you have broken the bar into two pieces, they become separate
problems, then it does not matter what order you do it---you will require 15 total breaks in
any scenario.

If, on the other hand, the assumption is that the whole bar stays together (as it would if
you were breaking it inside its wrapper, for instance), then you can do a little better.
You could simply break it along all axes (say, first the vertical and then the horizontal)
for a total of 6 breaks.

\ans{chomp-1}
\begin{comment}
Player~1 can always win. Here is a winning strategy for him: start by eating the square
at $(1,1)$.  Now, it is Player~2's turn and the remaining pieces are a
set of the form $\{ (x,y) | x = 0 \mbox{ or } y = 0, 0 \leq x, y \leq n \}$.
Either Player~2 eats the poisoned square or he eats a square at $(0,k)$ or
$(k,0)$, where $n \geq k >0$.  If Player~2 chooses $(0,k)$, Player~1 can eat the
square at $(k,0)$; if Player~2 chooses $(k,0)$, Player~1 can eat the square at
$(0,k)$. In either case, it is now Player~2's turn and the remaining pieces are
a set of the form $\{ (x,y) | x = 0 \mbox{ or } y = 0, 0 \leq x, y \leq k-1 \}$.
Eventually, this set will become $\{(0,0)\}$ and Player~2 will be forced to eat
the poisoned piece.

Given a $n \times n$ rectangle, in the upper right quadrant in the Cartesian
plane, with the lower leftmost point at $(0, 0)$. Two players take turns at taking a bite out of the
rectangle. A bite removes a square and all squares above and to the right. The first
player to eat the square at $(0, 0)$ loses. Construct a winning strategy for Player 1.
\end{comment}
Player~1 can always win. The key observation in this game is that we want to
force the play to be symmetrical around the diagonal, i.e.,
$(0,0), (1,1), \ldots, (n,n)$ with our opponent forced to move first in terms of breaking the symmetry. If that is
the case, we can follow each of his moves by a matching move reflected in this
diagonal which will eventually force him
to select the $(0,0)$ space.

The way to force this type of play is to be the first person to
select $(1,1)$---this causes the play area to be just the column $(0, [0-n])$
and the row $([0-n], 0)$ (i.e., an ``L'' shape). At that point, we can
successfully mirror any move that Player~2 makes, forcing him to eventually
choose (0,0).

% In pseudocode:
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true]
WinChomp():
    Choose (1,1)
    Until you win:
        Wait for Player 2 to choose square (i,j)
        Choose square (j,i)
\end{lstlisting}

\ans{chomp-2}
Suppose the set of remaining squares are of the form of a rectangle and one
additional square (which must be on the lower row) and Player~2 is to move.  The remaining set of squares will be of the form of a rectangle (if Player~2
plays the lower row) or a rectangle with a set of additional squares on the
lower row.  In either case, Player~1 can recreate the state to be a rectangle and one additional square, i.e., Player~1 can force a win.
By playing $(1,n-1)$ as his initial move, Player~1 can create this situation and therefore force a move.

\ans{chomp-3}
Suppose Player~2 has a winning strategy. Suppose Player~1 chose $(n-1,m-1)$ as
his initial choice and Player~2 countered with position $(i,j)$, leaving the set $S$ of squares.
Now, it is Player~1's turn and from this set, by hypothesis, Player~2 can force a win.  However Player~1 could have chosen $(i,j)$ as his initial
move and the set of remaining squares would be $S$ (since the square $(n-1,m-1)$ is above and to the right of all
other squares) with Player~2's turn.

This contradicts the hypothesis that Player~2 has a winning strategy; therefore Player~1 must have a winning strategy.

Note that this does not give an explicit strategy as we did for
Problem~\ref{chomp-1} and Problem~\ref{chomp-2}.

\ans{coins-1}
Number the coins from 1--50.
Player $F$ can choose all the even-numbered coins by first picking Coin~50 and then always picking the odd number coin at one of the two ends. For example, if Player $G$ chooses Coin~1, then in the next turn, Player $F$ chooses Coin~2. If Player $G$ chooses Coin~49, then $F$ chooses Coin~48 in the next turn. In this fashion, $F$ can always leave an arrangement where $G$ can only choose from odd-numbered coins.

If the value of the coins at even indices is larger that of the coins at odd indices,
$F$ can win by selecting the even indices and vice versa.  If the values
are the same, he can simply choose either and in each case, he cannot lose.

\ans{coins-2}
The problem can be solved using dynamic programming.

% When Player 1 is to move, the board consists of coins in the range
% $[2k+1,2l]$ and when Player 2 is to move, the coins are in the range
% $[2m,2n+1]$.

Let $P(m,n)$ be the largest margin of victory that
a player can achieve when the coins remaining are indexed by $m$ to $n$, inclusive.

The function $P$ satisfies the following:
\ifthenelse{\boolean{createspace}}{
\begin{eqnarray*}
P(m,n) & = & \max\Big(C[n] - P(n+1,m),C[m] - P(n,m -1)\Big) \; \mbox{if} \; n > m \\
P(m,m) & = & C[m] \; \mbox{if} \; n = m.
\end{eqnarray*}
}
{
\begin{eqnarray*}
\lefteqn{P(m,n)  = } \\
& & \max\Big(C[n] - P(n+1,m), \\
& & C[m] - P(n,m -1)\Big), \; \mbox{if} \; n > m \\
\lefteqn{P(m,m)  =  } \\
& & C[m], \; \mbox{if} \; n = m.
\end{eqnarray*}
}

In the general case, we can compute $P$ for $n$ coins by
dynamic programming---there are $n(n+1)/2$ possible arguments for $P$
and the work required to compute $P$ from previously computed values is constant. Hence $P$ can be computed in $O(n^2)$ time.

\ans{hiker}
The easiest way to prove this is to imagine another man (call him Bob)
descending the mountain on Saturday, in exactly the same fashion
as Adam did on Sunday.  When ascending on Saturday,
Adam will pass Bob at some time and place---this is the time and place which Adam
will be at on Sunday.

\chapter{ Probability}

\ans{sampling-1}
It is easy to solve this problem when $k=1$---we simply make one call to the random number
generator, take the returned $r$ value mod~$n$. We can swap $A[n-1]$ with $A[r]$.

For $k>1$, we start by choosing one element at random as above and we now
repeat the same process with the $n-1$ element subarray $A[0:n-2]$. Eventually, the random subset
occupies the slots $A[n-1-k:n-1]$ and the remaining elements are in
the first $n-k$ slots.

The algorithm clearly is in-place.
To show that all the subsets are equally likely, we prove something stronger, namely that all permutations of size $k$ are equally likely.

Formally, an $m$-permutation of a set $S$ of cardinality $n$ is a sequence of $m$
elements of $S$  with no repetitions.
Note that there are $\frac{n!}{(n-m)!}$ $k$-permutations.

The induction hypothesis now is that after iteration $m$,
the subarray $A[n-m-k:n-1]$ contains each possible $m$-permutation with probability
$\frac{(n-m)!}{n!}$.

For $m=1$, any element is equally likely to be selected, so the base case holds.

Suppose the inductive hypothesis holds for $m=l$.  Consider $m=l+1$.
Consider a particular $(l+1)$-permutation, say $\langle
\alpha_1,\ldots,\alpha_{l+1}\rangle$. This consists of a single element
$\alpha_1$ followed by the $l$-permutation
$\langle\alpha_2,\ldots,\alpha_{l+1}\rangle$. Let $E_1$ be the event that
$\alpha_1$ is selected in iteration $l+1$ and $E_2$ be the event that the first $l$ iterations produced $\langle \alpha_2,\ldots,\alpha_{l+1}\rangle$.
The probability of $\langle\alpha_1,\ldots,\alpha_{l+1}\rangle$ resulting after
iteration $l+1$ is simply $Pr( E_1 \cap E_2) = Pr( E_1 | E_2) \cdot Pr( E_2)$.
By the inductive hypothesis, the probability of permutation
$\langle\alpha_2,\ldots,\alpha_{l+1}\rangle$ is $\frac{(n-m)!}{n!}$. The
probability $Pr(E_1|E_2) =  \frac{1}{n-l}$ since the algorithm selects from elements in $0:n-l-1$ with equal probability. Therefore:

\ifthenelse{\boolean{createspace}}
{
\[
Pr( E_1 \cap E_2)  =   Pr( E_2 | E_1) \cdot Pr( E_1)
                     =  \frac{1}{n-l} \cdot \frac{( n - l)!}{n!}
                        = \frac{1}{\frac {(n-l-1)!}{n!}} .
\]
}
{
\begin{eqnarray*}
\lefteqn{Pr( E_1 \cap E_2)  } \\
 & = & Pr( E_2 | E_1) \cdot Pr( E_1) \\
 & = & \frac{1}{n-l} \cdot \frac{( n - l)!}{n!} \\
 & = & \frac{1}{\frac {(n-l-1)!}{n!}} .
\end{eqnarray*}
}

The algorithm generates all random $k$-permutations with equal probability,
from which it follows that all subsets of size $k$ are equally likely.

We make $k$ calls to the random number generator.  When $k$ is bigger than $\frac{n}{2}$,
we can optimize by computing a subset of $n-k$ elements to remove from the set.
When $k = n-1$, this replaces $n-2$ calls to the random number generator with a single call.

\ans{sampling-2}
We store the first $k$ packets. Consequently, we select the $n$-th packet
to add to our subset with probability $\frac{k}{n}$. If we do choose it,
we select an element uniformly at random to eject from the
subset.

To prove correctness, we use induction on
the number of packets that have been read. Specifically,
the inductive hypothesis is that all $k$-sized subsets are equally likely after
$n \geq k$.

The number of $k$-size subset is $\binom{n}{k}$,
so the probability of  any $k$-size subset is $\frac{1}{\binom{n}{k}}$.

For the base case $n=k$, there is exactly one subset of size $k$
which is what the algorithm has computed.

Assume the inductive hypothesis holds for $n > k$.  Suppose we have
processed the $n+1$-th packet.  The probability
of a $k$-size subset that does not include the $n+1$-th packet
is the probability that we had selected that subset after reading
the $n$-th iteration and did not select the $n+1$-th packet which is
\ifthenelse{\boolean{createspace}}{
\[
\frac{1}{\binom{n}{k}} \cdot \left( 1 - \frac{k}{n+1}\right) =
\frac{k! (n-k)!}{n!} \cdot \left( \frac{n-k+1}{n+1}\right) = \frac{k!\cdot (n-k)!\cdot (n-k+1)}{n!\cdot (n+1)} .
\]
}{
\begin{eqnarray*}
\lefteqn{\frac{1}{\binom{n}{k}} \cdot \left( 1 - \frac{k}{n+1}\right) } \\
 & = & \frac{k! (n-k)!}{n!} \cdot \left( \frac{n-k+1}{n+1}\right) \\
 & = & \frac{k!\cdot (n-k)!\cdot (n-k+1)}{n!\cdot (n+1)} .
\end{eqnarray*}
}
This equals $\frac{1}{\binom{n+1}{k}}$.
So, the inductive hypothesis holds for subsets excluding the $n+1$ element.

The probability
of a $k$-size subset $H$ that includes the $n+1$-th packet $p_{n+1}$
can be computed as follows: let $G$ be a $k$-size
subset of  the first $n$ packets. The only way we can
get from $G$ to $H$ is if $G$ contains $H - \{p_{n+1}\}$.
Let $G^\ast$ be such a subset; let $\{q\} = H - \{p_{n+1}\}$.

The probability of going from $G$ to $H$ is the probability
of selecting $p_{n+1}$ and dropping $q$ that is equal
to $\frac{k}{n+1}\cdot\frac{1}{k}$. There are $n+1-k$ candidate
subsets for $G^\ast$, each with probability $\frac{1}{\binom{n}{k}}$ (by the inductive hypothesis)
which means that the probability of $H$ is given by
\ifthenelse{\boolean{createspace}}{
\[
\frac{k}{n+1}\cdot\frac{1}{k}\cdot (n+1-k) \cdot \frac{1}{\binom{n}{k}} =
\frac{(n+1-k)(n-k)! k!}{(n+1)n!} = \binom{n+1}{k} ,
\]
}
{
\begin{eqnarray*}
\lefteqn{\frac{k}{n+1}\cdot\frac{1}{k}\cdot (n+1-k) \cdot \frac{1}{\binom{n}{k}} = } \\
 & & \frac{(n+1-k)(n-k)! k!}{(n+1)n!} = \binom{n+1}{k} ,
 \end{eqnarray*}
}
so induction goes through for subsets including the $n+1$-th element.

\begin{comment}
The algorithm is clearly correct for sequences of length $k$ or less.
To prove the algorithm is correct we use induction, starting from
sequences of length greater than or equal to $k+1$.

For the base case, namely $n=k+1$, the probability of element $k+1$ being in
the set is $\frac{k}{k+1}$, which is clearly correct. The probability of
one of the first $k$ elements remaining in the set is $1-\frac{1}{k+1} = \frac{k}{k+1}$,
so the base case holds.

Suppose the sampling is correct for $n=l$. We now compute the probability that
an element is in the sampled set after element $l+1$ is considered.
The probability that element $l+1$ is added is exactly $\frac{k}{l+1}$, which
is the desired probability.  The probability that element $k\leq l$ is in the
set after considering the element $l+1$ is the probability that
it was in the sampled set at iteration $l$ (which by induction is $\frac{k}{l+1}$),
and that it was not ejected at iteration $l+1$.
The probability of any element $i$ in the sampled
set being ejected at iteration $l+1$ is the probability of element $l+1$ being
selected times the probability that $i$ was chosen from the $k$ elements in
the sampled set to eject.
This is simply $\frac{k}{l+1}\cdot \frac{1}{k} =  \frac{1}{l+1}$.
Hence the probability of an element being present after step $l+1$ is
$\frac{k}{l}\cdot(1-\frac{1}{l+1}) = \frac{k}{l}\cdot\frac{l}{l+1}= \frac{k}{l+1}$
so induction goes through.
\end{comment}

\ans{sampling-3}
We can make use of the algorithm for
problem~\ref{sampling-1} with the array $A$ initialized by $A[i] = i$.
We do not actually need to store the elements in $A$, all we need to do is store
the elements as we select them, so the storage requirement is met.

\ans{perm-1}
The process does not yield all permutations with equal probability. One way to
see this is to consider the case $n=3$.  There are $3! = 6$ permutations possible.
There are a total of $3^3 = 27$ ways in
which we can choose the elements to swap and they are all
equally likely. Since 27 is not divisible by 6, some
permutations correspond to more ways than others, ergo not all permutations
are equally likely.

The process can be fixed by selecting elements at random and
moving them to the end, similar to how we proceeded in Problems~\ref{sampling-1}
and~\ref{sampling-3}.

\ans{perm-2}
Our solution to Problem~\ref{sampling-1} can be used with
$k=n$.  Although the subset that is returned is unique (it will be $\{0,1,\ldots,n-1\}$),
all $n!$ possible orderings of the elements in the set occur with equal
probability. (Note that we cannot use the trick to reduce the number of calls
to the random number generator at the end of Solution~\ref{sampling-1}.)

\ans{triangle}
The first thing to note is that three segments can make a triangle iff
no one segment is longer than the sum of the other two:
the ``only if'' follows from the triangle inequality and the ``if''
follows from a construction---take a segment and draw circles at the
endpoints
 with radius equal to the lengths of the other circles.

Since the three segment lengths add up to 1,
there is a segment that is longer than the sum of the other two iff
there is a segment that is longer than $\frac{1}{2}$.

Let $l = \min{(u1,u2)}$,
$m = \max{(u1,u2)} - \min{(u1,u2)}$, and $ u = 1 - \max{(u1,u2)}$;
these are the lengths of the first, second, and third segments,
from left to right.
If one segment is longer than 0.5, then none of the others
can be longer than 0.5; so, the events $l > 0.5$,
$m > 0.5$, and $u > 0.5$ are disjoint.

Observe that $l > 0.5$ iff both $u1$ and $u2$ are greater than 0.5;
the probability of this event is $\frac{1}{2} \times \frac{1}{2}$ because $u1$ and $u2$ are chosen independently.
Similarly $m > 0.5$ iff both $u1$ and $u2$ are less than 0.5, which is $\frac{1}{2} \times \frac{1}{2}$.

To compute the probability of $m > 0.5$, first we consider the case
that $u1 < u2$. For $m > 0.5$, we need $u1$ to be between 0 and $1$ and $u2$ to be between $0.5 + u1$ and $1$. This probability can
be expressed by the integral
\[
\int_{u1=0}^{0.5} \int_{u2=u1+0.5}^{1} 1 \cdot du1 \cdot du2
\]
which evaluates to $\frac{1}{8}$.

By symmetry, the probability of $m > 0.5$ when $u1 > u2$ is also $\frac{1}{8}$.
Hence the probability of a segment being longer than $\frac{1}{2}$
is $\frac{1}{4} + \frac{1}{4} + \frac{1}{4} = \frac{3}{4}$.
So, the probability of being able to make a triangle out of the segments
is $1 - \frac{3}{4} = \frac{1}{4}$.

For the second case, we fail to be able to make a triangle
in case $u1 > 0.5$, $u2 - u1 > 0.5$, or $1-u2 > 0.5$. The first
probability is simply $\frac{1}{2}$.

The second probability is given by the integral
\[
\int _{u1=0}^{0.5} \int_{u2=u1+0.5}^{1} \frac{1}{(1-u1)} \cdot du2 \cdot du1 .
\]
Note that the probability density function for $u2$ is different from the previous
case since $u2$ is uniform in $[u1,1]$, not $[0,1]$.
This integral evaluates to $\frac{1 + \log_e{\frac{1}{2}}}{2}$.
The third probability can also be computed using an integral but by symmetry,
it must be the same as the second probability.
Hence the final probability is $\frac{1}{2} + 2 \cdot \frac{1 + \log_e{\frac{1}{2}}}{2} \approx 0.807$.

Intuitively, the second formulation leads to a higher probability of a long line segment
because there is less diversity in the points. For the first case, the points are spread
randomly; for the second, there is a 0.5 chance that the first point itself precludes
us from building the triangle.  Another way to think of it is that if we put down a lot of points, the first method will lead to short segments with little variation in lengths but the second method will give us a skewed distribution and the first few segments will be considerably longer.

These computations can be verified by a numerical simulation. Here is an example code to perform this:
\lstinputlisting[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]{triangle.java}

\ans{balls-bins}
The probability that a given ball does not land up in a given bin is
$(n-1)/n$. The probability that none of the balls land up in the bin
is $\left(\frac{n-1}{n}\right)^m$.  Hence the expected number of empty
bins can be given as $n\left(\frac{n-1}{n}\right)^m$. Note that this
can be closely approximated by $n\cdot e^{m/n}$. Hence as long as on
an average, each server is handling significantly more than one
client, there should be very few idle servers.

\ans{random-permutation}
Let $X_i$ be the random variable, which is $1$ if $\sigma(i) = i$
and $0$ otherwise. (Such a random variable is often referred to as an ``indicator random variable''.) The number of fixed points is equal to $X_1 + X_2 + \cdots + X_n$. Expectation is linear, i.e., the expected value
of a sum of random variables is equal to the sum
of the expected values of the individual random variables. The
expected value of $X_i$ is $0 \cdot \frac{n-1}{n} + 1 \cdot \frac{1}{n}$ (since
an element is equally likely to be mapped to any other element).
Therefore the expected number of fixed points is $n \cdot \frac{1}{n} = 1$.

We can compute the expected length of $\mu$ by defining
indicator random variables $Y_1,\ldots Y_n$, where $Y_i = 1$ iff
$\forall j < i \; \big(\sigma(j) < \sigma(i)\big)$.
Observe that the length of $\mu$ is simply the sum of the $Y_i$s.
The expected value of $Y_i$ is $\frac{1}{i}$, since $\forall j < i \; \big(\sigma(j) < \sigma(i)\big)$
iff the largest of the first $i$ elements is at position $i$, which
has probability $\frac{1}{i}$ since all the permutations are equally likely.
Therefore the expected value for the length of $\mu$ is $1 + \frac{1}{2} + \frac{1}{3} + \cdots + \frac{1}{n}$, which tends to $\log_e n$.

Note that for both parts of the problem, we used the linearity
of expectation which does not require the individual random variables
to be independent.  This is crucial since the $X_i$s and $Y_j$s
are not independent---for example, if the first $n-1$ elements get mapped
to themselves, then the $n$-th element must also map to itself.

\begin{comment}

Ian's solution (INCORRECT)

Does the following process yield a uniformly random permutation of $A$?
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true]
For i = 1::n swap A[i] with a randomly chosen element of A.
  (The randomly chosen element could be i itself.)
\end{lstlisting}

My first guess would be that it would (assuming it's a 1-based array); at each turn, the
probability that any given value $j$ will be selected as the right-hand side of the swap is
1/n; earlier values in the array have the same chance of being permuted at each stage as
later values do. Each array slot is visited once in order, so every element will be
permuted an average of 2 times, and in both cases the previous swaps have no impact on
where it will be swapped to.

Here's a Python implementation of randomly permuting an array:

\begin{verbatim}
import random

def Permute(A):
    for i in range(len(A)):
        j = random.randint(0, len(A) - 1)
        temp = A[i]
        A[i] = A[j]
        A[j] = temp
    return A
\end{verbatim}

To test whether there appears to be any skew in the resulting values,
this routine uses repeated random permutations and computes an
average of the resulting values:

\begin{verbatim}
def TestSkew(trials, size):
    # loop over permuting the same array; sum and then get average at the end
    A = range(size)
    random.shuffle(A)
    B = A[:]
    for i in range(trials):
        A = Permute(A)
        # update the summed values in B
        for j in range(len(B)):
            B[j] = B[j] + A[j]
    for k in range(len(B)):
        B[k] = B[k] / trials
    return B
\end{verbatim}

The result of permuting a 100 item array 5000 times is:

\begin{verbatim}
TestSkew(5000, 100) =
[49, 49, 48, 49, 49, 49, 50, 49, 49, 49, 48, 49, 50, 49, 49, 49, 48, 49, 49, 49, 48,
 49, 49, 49, 49, 49, 49, 49, 49, 50, 49, 49, 48, 49, 49, 49, 49, 49, 49, 49, 49, 50,
 48, 49, 49, 49, 49, 49, 48, 49, 50, 49, 49, 49, 49, 49, 48, 49, 49, 49, 49, 49, 49,
 50, 49, 49, 48, 50, 49, 49, 49, 50, 49, 51, 49, 50, 49, 50, 48, 49, 49, 48, 49, 49,
 49, 49, 50, 49, 49, 49, 49, 49, 49, 49, 49, 50, 49, 49, 49, 48]
\end{verbatim}

There does not appear to be much skew - the median
value of 49 (the range is 0 to 99) is in almost every slot, and
there doesn't appear to be a pattern that I can discern in the numbers,
other than it being a uniform distribution.
\end{comment}

\ans{rand-1}
Basically, we want to produce a random number between $0$ and $b-a$, inclusive.

We can produce a random number from $0$ to $l-1$ as follows: let $j$ be the least
integer such that $l\leq 2^j$.

If $l$ is a power of 2, say $l=2^j$, then all we need are $j$ calls to the 0-1 valued
random number generator---the $j$ bits from the calls encode a $j$ bit integer
from $0$ to $l-1$, inclusive and all such numbers are equally likely;
so, we can use this integer.

If $l$ is not a power of 2, the $j$ calls may or may not encode an integer in the range
$0$ to $l-1$. If the number is in the range, we return it; since all the numbers
are equally likely, the result is correct.

If the number is not in the range, we try again. The probability of having to try again
is less than $\frac{1}{2}$ since $l > 2^{j-1}$.
The probability that we take exactly $k$ steps before succeeding
is at least $(1 - \frac{1}{2})^{k-1}\cdot \frac{1}{2} = \frac{1}{2}^k$.
The expected number of trials
before we converge to a solution is bounded by $1\cdot\frac{1}{2} + 2\cdot(\frac{1}{2})^2 +
(\frac{1}{2})^3 + \cdots $ whose limit is 2.

\ans{hist-1}
Let $F_X(x)$ be the cumulative distribution function for $X$,
i.e., $F_X(x) = \mbox{probability that } X \leq x$.

To generate $X$, we perform the following operation: we select a number $r$ uniformly
at random in the unit interval. We then project back from $F_X$ to obtain a value
for $X$, i.e., we return $s = F_X^{-1}(r)$.

By construction, the probability that the value we return is less than or equal to $\alpha$ is
$F_X(\alpha)$, so the cumulative distribution function of the random variable we
created is exactly that of $X$.

\ans{dice-completion}
First we prove that if $\langle X_1, X_2,\ldots \rangle$ is a sequence of Bernoulli IID random variables, with $p(X_i = 1) = p$,
then the expected time to see the first $1$ is $\frac{1}{p}$. The reasoning is as follows: define $F_i$ to
be the event that the first $1$ comes on the $i$-th trial. Then $Pr(F_i) = (1-p)^{i-1} \cdot p$.
Hence the expected time is $S = \sum_{i=1} i \cdot  (1-p)^{i-1} \cdot p$. This sum simplifies to $\frac{1}{p}$ (multiply
both sides by $p$, subtract, and sum the infinite geometric series on the right).

Now, we consider the problem of dice rolls. The key is to determine the expected time to see the
$k$-th new value. Clearly, the expected time to see the first new value is just $1$. The time to see
the second new value from the first new value is $\frac{1}{5/6}$ since the probability of
seeing a new value, given that one value has already been seen, is $5/6$. In this way, the time
taken to see the third new value, given that two values have already been seen, is $\frac{1}{4/6}$.
Generalizing this idea, the time taken to see the $k$-th new value, given that $k-1$ values have already been seen, is $\frac{1}{(6- (k-1)) /6}$.

Hence the expected time to see the sixth new value is $\frac{6}{6} + \frac{6}{5} + \frac{6}{4} + \frac{6}{3} + \frac{6}{2} + \frac{6}{1}
\approx 14.7$.

\ans{option-pricing-2}
Let $f$ be the price for the option.
A fair price is determined by the no-arbitrage requirement.
Suppose we start with a portfolio of $x$ shares
and $y$ options in $S$---$x$ and $y$ may be
negative (which indicates that we sell stocks or sell options).

The initial value of our portfolio is $x\cdot 100 +y\cdot f$.
On Day 100, two things may have happened:
\begin{itemize}
\item The stock went up and the portfolio is worth $x\cdot 120 + y\cdot 20$.
\item The stock went down and the portfolio is worth  $x\cdot 70$.
\end{itemize}
If we could choose $x$ and $y$ in such a way that our initial portfolio
has a negative value---which means that we are paid to take it on---and
regardless of the movement in the stock, our portfolio takes a
nonnegative value, then we will have created an arbitrage.

Therefore the conditions for an arbitrage to exist are:
\begin{eqnarray*}
x\cdot 120 + y\cdot 20 & \geq & 0 \\
x\cdot 70 & \geq & 0 \\
x\cdot 100 + y \cdot f  & < & 0
\end{eqnarray*}
A fair price for the option is one in which no arbitrage exists.
% The second equation implies that $x\geq 0$

If $f$ is less than $0$, an arbitrage exists---we are paid to buy options,
lose nothing if the price goes down, and make \$20 per option
if the price goes up. Therefore $f \geq 0$, so we can write the third
inequality as $y \leq -\frac{100}{f} x$.
The first equation can be rewritten as $y \geq -6\cdot x$.

Combining these two inequalities, we see that an arbitrage does not exist
if $-\frac{100}{f} \geq -6$, i.e., $f \leq \frac{100}{6}$. Outside of the
interval $[0,\frac{100}{6}]$, we do have an arbitrage.

For example, if $f=19 > \frac{100}{6}$, then the option is overpriced and
we should sell (``write'') options.  If we write $b$ options and buy one share,
we will start with a portfolio that is worth  $100 + 19\cdot b$.
If the stock goes down, the options are worthless; so, our portfolio
is worth $\$70$. If the stock goes up, we lose $\$20$ on
each option we wrote but see a gain on the stock we bought. We want
the net gain to be nonnegative and the initial
portfolio to have a negative value, i.e.,
\begin{eqnarray*}
120 + 20 \cdot b & \geq & 0 \\
100 + 19 \cdot b  & < & 0
\end{eqnarray*}
Combining the two inequalities, we see that any value of $b$ in $[-6,-\frac{100}{19})$
leads to an arbitrage.

\ans{option-pricing-3}
Suppose our initial portfolio consists of $x_0$ stocks, $x_1$ options, and $x_2$
bonds.

Proceeding as above, we see the condition for an arbitrage to exist
is:
\begin{eqnarray*}
100 \cdot x_0 + f \cdot x_1 + x_2 & < & 0 \\
120 \cdot x_0 + 20 \cdot x_1 + 1.02 \cdot x_2 \geq 0 \\
70 \cdot x_0 + 1.02 \cdot x_2 \geq 0
\end{eqnarray*}
Writing the linear terms as $Ax$, we see that
if $det(A) \neq 0$, then we can always find an arbitrage since we
can solve $Ax = b$. We will denote row~$i$ of $A$ by $A_i$.

The determinant of $A$ equals $70(1.02f - 20) + 1.02(100\cdot20 - 120f)$.
This equals $0$ when $f = 640/51 \approx 12.549 = f^\ast$, so an arbitrage
definitely exists if the option price is not equal to $f^\ast$.

Conversely, if the option is priced at $f^\ast$, $det(A) = 0$ and
in particular $A_0 = 0.6275 \cdot A_1 + 0.3583 \cdot A_2$.
Since $A_0$ is a linear combination of
$A_1$ and $A_2$ with positive weights, then
if $A_1 x\geq 0$ and $A_2 x \geq 0$, $A_0x$ must also be $\geq 0$, so
no arbitrage can exist.

\ans{option-pricing-1}
Let $x$ be the price of the stock on day 100. The option is worthless if $x < 300$.
If the price is $x \geq 300$, the option is worth $x -300$ dollars.
The expected value of $x$ is given by the integral
\[
\int_{300}^{\infty} (x-300) \cdot \frac{e^{-\frac{(x-300)^2}{2\cdot (20)^2}}}{\sqrt{2 \pi (20)^2}} dx .
\]

% expectation of half normal is \sigma \sqrt{2/\pi} ->  for just the positive side it is half
% of this i.e., \sigma \sqrt{1/(2 \pi)}

The integral can be evaluated in closed form---let $y = x -300$ and
let's write $\sigma$ instead of $20$.
The expression above simplifies to
\[
\int_{0}^{\infty} y \cdot \frac{e^{-\frac{y^2}{2\cdot \sigma^2}}}{\sqrt{2 \pi \sigma^2}} dy .
\]
The indefinite integral $\int w\cdot e^{-w^2} dw$ has the closed form solution $-\frac{e^{-w^2}}{2}$, so the
definite integral equals $\sigma \sqrt{\frac{1}{2 \pi}} \approx 0.39 \sigma$.
Therefore the expected payoff on the option on day 100 is $0.39 \cdot 20 =
\$7.8$.

\ans{optimum-bidding}
The first thing to ask is what are you trying to optimize? There
are various objectives, all of which are reasonable---maximize expected
profit, minimize loss, maximize ratio of expected profit to variance, etc.

Let's say we want to maximize profit. The expected profit is
$\int_{X = B }^{X=400} ( 1.8 X - B)\cdot \frac{1}{400} dB$.
This simplifies to $\frac{0.9 \cdot 400^2 - 400 B + 0.1 B^2}{400}$.
The derivative is $0.2B - 400$.

The expected profit has a negative derivative in the range of interest---$B \in [0,400]$.
This means that as we increase $B$, we get less and less profit, so we should keep $B=0$.

In retrospect, this result makes sense since if we win the auction, we are
paying twice of $X$ in expectation and getting only $1.8X$ in return.

\ans{once-or-twice}
If the probability of winning is $p$, then the expected gain is $-1
+ p\cdot w$. Hence for a fair game, $w = 1/p$.

The face value of the card can be any number between $1$ and $13$. For the dealer, all values are equally likely. Hence if the
player's card has a face value $i$, then the probability of winning for
the player is $(i-1)/13$. If the player always takes only one random card,
his probability of winning is $(1/13)\sum_{i=1}^{13}{(i-1)/13} =
6/13$. Hence it makes sense to ask for the next card only if the first
card yields a probability less than $6/13$, i.e., the face value of the first
card is $7$. If we are given that the face value of the first card is $7$ or
more, then the chances of winning are $(1/7)\sum_{i=7}^{13}{(i-1)/13} = 9/13$; otherwise, it is $6/13$. Hence the
overall probability of winning is
$\frac{7}{13}\cdot\frac{9}{13} + \frac{6}{13}\cdot\frac{6}{13} = 99/169$.
Thus the fair value would be $169/99 \approx 1.707$.

\begin{comment}

Clearly, the optimum strategy of each player is independent of each other
since they do not learn any thing new about the other player's
moves. By symmetry, we can argue that the best chances of winning is
going to be $1/2$. We can prove this by contradiction---if there
exists a strategy that results in your chances of winning against a
rational adversary being greater than $1/2$ then the rational
adversary can also use the same strategy and hence the chances of
winning cannot be greater than $1/2$.

Suppose  the adversary uses a certain strategy $A$ which results in a
certain probability distribution for the adversary.  Suppose that the
first number you get as a player is $c$.  If choose to keep $c$ as the
final number, the probability of winning will monotonically increase
with $c$. If you choose to get the second random number, given the
probability distribution of the adversarie's number, the probability
of winning would be certain fixed number. Clearly, there exists a
number $\hat{c}$ such that if $c < \hat{c}$ it is better to go for the
second number otherwise it is better to keep the first number.

Now that we have established that the best strategy is to find a
number $\hat{c}$ such that we choose the second number if and only if
the first number is less than $\hat{c}$, we will try to find the
optimum value of $\hat{c}$.  Suppose the probability distribution for the adversary's
number strategy is $A(x)$ then if you use

Let's say that the random numbers you
get are $x_1$ and $y_1$ and the numbers that the adversary gets are
$x_2$ and $y_2$. Suppose you pick the number to be $c_1$
and the adversary picks the number to be $c_2$. Then the chances of
winning is going to be
\begin{eqnarray*}
Pr[(x_1 \ge c_1) \wedge (x_2 \ge c_2) \wedge (x_1 > x_2) ]  &+&\\
 Pr[(x_1 \ge c_1) \wedge (x_2 < c_1) \wedge (x_1 > y_2)]  &+&\\
Pr[(x_1 < c_1) \wedge (x_2 \ge  c_1) \wedge (y_1 > x_2)]
 &+&\\
Pr[(x_1 < c_1) \wedge (x_2 <  c_1) \wedge (y_1 > y_2)]. &&
\end{eqnarray*}

Let's say  we choose a second number if the first
number we are given is below a threshold, $\alpha$.
Let the final number we get be $M$.
Then the CDF for our choice $M_1$ is given by:
\begin{eqnarray*}
P(M_1 \leq t ) & = & \alpha \cdot t, 0 \leq t < \alpha \\
P( M_1 \leq t ) & = & \alpha^2 + (t-\alpha) + \alpha\cdot(t -\alpha)
\end{eqnarray*}
The first probability comes from the recognition that
we need the first number selected to be  less than $\alpha$ and
the second to be less than $t$.

The second probability comes from writing $P(M_1 \leq t) =
P(M_1 \in [0,\alpha) \cup M_1 \in [\alpha,t]$ with the
two subevents being disjoint.

Differentiating the CDF, we see the Probability Distribution Function    (PDF) for $M_1$ is given by
\begin{eqnarray*}
p_{M_1}(t) & = & \alpha, t < \alpha \\
p_{M_1}(t) & = & 1-\alpha, t \geq \alpha
\end{eqnarray*}

Suppose we choose $\alpha_1$ as our threshold and our opponent
chooses $\alpha_2$ as his threshold.  Let his final
number be $M_2$. Then the probability that $M_1 > M_2$ can
be computed as follows:

\end{comment}

\ans{red-card}
We can trivially achieve a probability of success of $\frac{1}{2}$ by always choosing the first card.

A natural way to proceed is to consider the probability
$p_k(f)$ of winning for the optimum strategy after $k$ cards
remain, of which $f$ are red cards.
Then $p_k(f) = \max\big(\frac{f}{k},
\frac{f}{k}\cdot p_{k-1}(f-1) + (1-\frac{f}{k})\cdot p_{k-1}(f)\big)$.

The base cases for the recurrence are $p_1(1) = 1$ and $p_1(0) = 0$.
Applying the recurrence, we obtain $p_2(2) = 1, p_2(1) = \frac{1}{2},
p_2(0) = 0$, and $p_3(3) = 1, p_3(2) = \frac{2}{3}, p_3(1) = \frac{1}{3},
p_3(0) = 0$.  This suggests that $p_k(f) = \frac{f}{k}$, which
can directly be verified from the recurrence. Therefore the best
we can do, $p_{52}(26) = \frac{26}{52} = \frac{1}{2}$, is no better than simply selecting
the first card.

An alternate view of this is that since the cards in the deck are
randomly ordered, the odds of the top card we select being red is the same
as the card at the bottom of the deck being red, which has a $\frac{f}{k}$ chance of
being red when there are $f$ red cards and $k$ cards in total.

\ans{secy-problem}
If we always select the first secretary, we have a $\frac{1}{n}$ chance of
selecting the best secretary.

One way to do better is to skip the first $\frac{n}{2}$ secretaries
and then choose the first one in the remaining set that is superior to
the best secretary interviewed in the first $\frac{n}{2}$ secretaries. This
has a probability of succeeding of at least $\frac{1}{4}$ since
the probability that the second best secretary lies in the first half
and the best secretary is in the second half is at least $\frac{1}{4}$. Note that the probability of this is actually more than $\frac{1}{4}$
since the second best secretary is in the first half, there is a higher than
$0.5$ probability that the best secretary is in the second half.

It is known that if we follow a strategy of skipping the first
$s$ secretaries and selecting the first secretary who is superior to all others so
far, the probability is maximized for $s$ closest to $n/e$ and the maximum probability
tends to  $1/e$.

\ans{which-coin}
Let $L$ be the event that the selected coin is tail-biased, $U$ be the
event that the selected coin is head-biased, and $3H5$ be the event
that a coin chosen at random from the bag comes up heads $3$ times out of $5$ tosses.

We want to compute $Pr( L | 3H5)$. By Bayes' rule, this is $\frac{Pr( L \cap 3H5) }{Pr(3H5)}$.
Applying Bayes' rule again, this probability equals
\begin{eqnarray*}
\lefteqn{\frac{Pr(3H5|L)\cdot Pr(L)}{Pr\big(3H5\cap(L \cup U)\big)}} \\
& = & \frac{Pr(3H5|L)\cdot Pr(L)}{Pr(3H5 \cap L) + Pr(3H5 \cap U)} \\
& = &  \frac{Pr(3H5|L)\cdot Pr(L)}{Pr(3H5|L)\cdot Pr(L) + Pr(3H5|H)\cdot Pr(H)} \\
& = & \frac{\binom{5}{3}\cdot0.4^3\cdot 0.6^2\cdot 0.5}{\binom{5}{3}\cdot0.4^3\cdot 0.6^2\cdot 0.5 + \binom{5}{3}\cdot0.4^2\cdot 0.6^3\cdot 0.5} \\
& = & 0.4
\end{eqnarray*}

For the second part, we can use the Chebyshev inequality to compute the number of trials we need for
a majority of $n$ tosses of the tail-biased coin to be heads with probability $\frac{1}{100}$.
Let $L_i$ be the event that the $i$-th toss of the tail-biased coin comes up heads.
It will be convenient to use a Bernoulli random variable $X_i$ to encode
this event, with a $1$ indicating heads and $0$ indicating tails.

The mean $\mu$ of the sum $X$ of
$n$ Bernoulli random variables which are IID with probability $p$ is
$n\cdot p$; the standard deviation $\sigma$ is $\sqrt{np(1-p)}$. In our context,
$\mu = 0.4n$ and $\sigma = \sqrt{6n/25}$.

The Chebyshev inequality gives us a bound on the probability of a random variable
being far from its mean. Specifically, $Pr\big( |X - \mu| \geq k \sigma \big) \leq \frac{1}{k^2}$.

For the majority of $n$ tosses to not be tails, it is necessary that
the sum of the $n$ coin tosses is greater than or equal to $0.5n$.
We want to bound this probability by $\frac{1}{100}$, so we take $k=10$.
We want to solve for $n$ such that $0.5n -  0.4n \geq 10 \cdot \sqrt{6n/25}$, i.e.,
$0.1n \geq 4.9 \sqrt n$ which is satisfied for $n \geq 2400$.
Note that the analysis is not tight---the Chebyshev inequality refers to the probability of $|X - \mu| \geq k \sigma$ but we are only looking at $X - \mu \geq \sigma$.

The Chebyshev inequality holds for all random variables if they have a variance. We can obtain
a tighter bound by applying a Chernoff bound, which is specific to the sums of Bernoulli random variables.
Specifically, Chernoff bounds tell us that $Pr\big(X \geq (1+\delta)\mu \big) \leq e^{\frac{-\mu\delta^2}{3}}$.
We want to bound $Pr\big(X \geq 0.5n = (1+0.25)(0.4n)\big)$, so
$\delta = 0.25$. Thus we want
$e^{\frac{-0.4n (0.25)^2}{3}} < 0.01$; taking natural logs we obtain $-\frac{0.4n (0.25)^2}{3} < \ln{100} = -4.6$, which holds for $n > 552$.

The Chernoff bound is also pessimistic---through simulation code attached below,
we determined that when $n=553$, only $17$ times in $10^7$ trials did we
see a majority of tails; when $n=148$, tails was not a majority in $0.88$\%
of the trials.
\lstinputlisting[basicstyle=\footnotesize,numbers=left,breaklines=true,language=Java]{TailCoin.java}

\ans{rand-complexity}
First, we show that any deterministic algorithm must examine all
Boolean variables.
The idea is that an adversary can force the value of any subexpression
to be unknown till all the variables in the subexpression have been read.
For example, suppose variable $X$ is ANDed with variable $Y$.
If the algorithm reads the value of $X$ before $Y$,
we return true; when $Y$ is queried, we return false.
In this way, the value of $X\wedge Y$ is determined
only after both the variables are read.

This generalizes with induction: the inductive hypothesis is that
an $L_k$ expression requires all the variables to have been read
before its value is determined and its final value is the
value of the last variable read.
For a subexpression of the form $\phi \wedge \psi$, where $\phi$ and
$\psi$ are $L_k$ expressions, if all the variables from $\phi$
are read before all the variables from $\psi$ are read, the adversary
chooses the last variable read from $\phi$ to be true, forcing
the algorithm to evaluate $\psi$. A similar argument can be used
for subexpressions of the form $\phi \vee \psi$.

Suppose we evaluate an expression by choosing
one of its two subexpressions at random to evaluate first;
we evaluate the other subexpression only if the expression's value
is not forced by the subexpression that we evaluated first.

For example, if we are to evaluate an $L_{k+1}$ expression of the form
$\big((\phi_0 \wedge \phi_1) \vee (\psi_0 \wedge \psi_1)\big)$,
where the subexpressions $\phi_0,\phi_1,\psi_0,\psi_1$ are $L_k$ expressions, we
randomly choose one of $(\phi_0\wedge\phi_1)$ and $(\psi_0 \wedge \psi_1)$
to evaluate first.  If the first expression evaluated is true, we can ignore the
second; otherwise, we evaluate the second.
If the first expression is true, we reduced the number of variables queried
by at least half. If the first expression is false, at least
one of the two subexpressions is false and we have a probability of $0.5$
of selecting that subexpression and avoiding evaluating the other subexpression.
So, in the worst-case, we can expect to avoid one of the four
subexpressions $\phi_0,\phi_1,\psi_0,\psi_1$. Therefore the expected
number of variables queried to evaluate an $L_{k+1}$ expression, $Q(k+1)$ satisfies
\[
Q(k+1) \leq 3 \cdot Q(k) .
\]
From this, $Q(k) = 3^k$. It is straightforward to use induction
to show that there are a total of $n = 4^k$ variables in an $L_k$ expression, so
$Q(k) = n^{\log_4 3} = n^{0.793}$.

\chapter{ Programming}
\lstset{language=C++}

\ans{parity}
The fastest algorithm for manipulating bits can vary based on the underlying hardware.

The time taken to directly compute the parity of a single number is proportional
to the number of bits:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
short parity(long a) {
  short result = 0;
  for (; a != 0; a = a / 2) {
    result = result ^ (a & 1);
  }
  return result;
}
\end{lstlisting}

A neat trick that erases the least significant bit of a number
in a single operation can be used to improve performance in the
best and average cases:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
short parity2(long a) {
  short result = 0;
  while (a) {
    result ^= 1;
    a = a & (a - 1);
  }
  return result;
}
\end{lstlisting}

But when you have to perform a large number of parity operations and more generally,
any kind of bit fiddling operation, the best way to do this is to precompute the
answer and store it in an array.
Depending upon how much memory is at your disposal (and how much fits
efficiently in cache), you can vary the size of the lookup table. Below is an example
implementation where you build a lookup table ``precomputed\_parity''
that stores the parity of any $16$-bit number $i$ as
$precomputed\_parity[i]$. This array can either be constructed during
static initialization or dynamically---a flag bit can be used
to indicate if the entry at a location is uninitialized.
Once you have this array, you can implement the parity function as follows:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
short parity3(long a) {
  short result = precomputed_parity[a >>16];
  result ^= precomputed_parity[a & 0xFFFF];
  return result;
}
\end{lstlisting}

\ans{rev-bits}
Similar to computing parity (cf.~Problem~\ref{parity}), the fastest way
to reverse bits would be to build a precomputed array \texttt{precomputed\_reverse}
such that for every $16$-bit number $i$,  \texttt{precomputed\_reverse[i]} holds the bit-reversed~$i$.
Then you can do something like this:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
long reverse_bits(long l) {
  return (precomputed_reverse[l & 0xFFFF] << 16) |
    precomputed_reverse[l >> 16] ;
}
\end{lstlisting}

\ans{run-length-encoding}
Again, here precomputed arrays can speed things significantly.
For all possible 256 values of a byte, we can store the corresponding
run-length encoded values.  One tricky thing here is that a particular
sequence of identical consecutive bits may cross the byte boundary  and you may need to combine the results
across the byte boundaries. This just requires some additional logic
to see if the last bit of the previous byte matches the first bit of the
current byte or not and accordingly either simply concatenate the
encoded sequence or add the first number for the current byte to the last
number for the previous byte.

\ans{perm}
We can use the fact that every permutation
can be expressed as a composition of disjoint cycles, with
the decomposition being unique up to ordering.

For example, the permutation $(3,1,2,4)$ can be represented as $(1,3,2)(4)$, i.e., we
can achieve the permutation $(3,1,2,4)$ by these two moves: $1\mapsto 3, 3\mapsto 2, 2\mapsto 1$ and $4\mapsto 4$.

If the permutation was given to us as a set of disjoint cycles, we could easily apply the
permutation in constant amount of additional storage since we just
need to perform rotation by one element. So, what remains is a way
to identify the disjoint cycles that constitute the permutation.

Again, it is fairly easy to identify the set of cycles if you have
an additional $N$ bits: you start from any position and keep going
forward (from $i$ to $A[i]$)
till you hit the initial index, at which point you have found one of the
cycles.  Then you can go to another position that is not already a part
of any cycle.  Finding a position that is not already a part of a cycle
is easy if you have a bit-vector that could indicate whether we
have already included a given position in a cycle or not.

One way to do this without using additional $O(N)$ storage could be to use
the sign bit in the integers that constitute the permutation:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
void ApplyPermutation(int * permutation, int* A, int n) {
  for (int i = 0; i < n; ++i) {
    if (permutation[i] > 0) {
      // Start searching for a cycle from i.
      int j = i;
      int tmp = A[i];
      do {
        int k = permutation[j];
        int swap_var = A[k];
        A[k] = tmp;
        tmp = swap_var;
        // Mark j as visited.
        permutation[j] *= -1; // sets the sign bit
        j = k;
      } while(j != i);
    }
  }
  // Restore the sign for permutation.
  for (int i = 0; i < n; ++i) {
    permutation[i] *= -1;
  }
}
\end{lstlisting}

The above code will apply the permutation in $O(N)$ time but implicitly
we are using additional $O(N)$ storage (even if we are borrowing it
from the sign bit of permutation matrix).
We need $O(N)$ storage to remember all the visited cycles.

We can avoid this by just going from
left to right and applying the cycle only if the current position is the leftmost position in
the cycle. In order to test whether the current
position is the leftmost position or not, you will have to traverse the
cycle once more. This boosts the runtime to $\Theta(N^2)$.
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
void ApplyPermutation2(int * permutation, int* A, int n) {
  for (int i = 0; i < n; ++i) {
    // Traverse the cycle to see if i is the min element
    bool min_element = true;
    int j = permutation[i];
    while( j != i) {
      if (j < i) {
        min_element = false;
        break;
      }
      j = permutation[j];
    }
    if (min_element) {
      int j = i;
      int tmp = A[i];
      do {
        int k = permutation[j];
        int swap_var = A[k];
        A[k] = tmp;
        tmp = swap_var;
        j = k;
      } while(j != i);
    }
  }
}
\end{lstlisting}

\ans{inv-perm}
The solution is very similar to the previous problem. All you need to
do is decompose the permutation into a set of cycles and invert each
cycle one step back. For example, the permutation  $3,1,2,4$ can be
represented as $(1,3,2)(4)$.
Hence the inverse can be represented as $(2,3,1)(4)$ which amounts to
$2,3,1,4$.

In order to save additional space, we can use exactly the same set of
tricks as in the above problem.

\ans{reverse-words}
If you try to figure out the position for each character in a single
pass, it becomes fairly complex. If you do this in two stages, it
becomes fairly easy. In the first step, invert the entire string and in
the second step, invert each word. For example, $ram\; is\; costly \mapsto
yltsoc\; si\; mar \mapsto costly\; is\; ram$. Here is an example code that achieves
this:
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
void InvertString(char* input, size_t length) {
  for (int i = 0; i < length /2; ++i) {
    swap(input + i, input + length - i - 1);
  }
}

void ReverseWords(char* input) {
  size_t length = strlen(input);
  InvertString(input, length);
  int start = 0;
  while(start < length) {
    int end = start;
    while(end < length && input[end] != ' ') {
      end++;
    }
    InvertString(input+start, end-start);
    start = end + 1;
  }
}
 \end{lstlisting}

\begin{comment}
Solution from Ian Varley

Invert all the words in a sentence: ``it was the best of times''
$\mapsto$ ``ti saw eht tseb fo semit''

The solution to this problem differs if we need to do it in-place or not. Assuming we do
not, a Python solution is simple:

\begin{verbatim}
def invert(sentence):
    words = sentence.split()
    output = []
    for word in words:
        output.append(reverse(word))
    return ' '.join(output)

def reverse(word):
    l = list(word)
    l.reverse()
    return ''.join(l)
\end{verbatim}

Sample run:
\begin{verbatim}
>> invert("it was the best of times")

ti saw eht tseb fo semit
\end{verbatim}

This algorithm should run in $O(n+m)$ time where $n$ is the number of words and $m$ is the
number of characters. The outer loop does a constant number of operations for each word,
and the inner loop does a constant number of operations for each letter (assuming
``reverse'' is implemented in $O(n)$ time).
\end{comment}

\ans{rev-list}
Here is an example code that reverses a linked list and returns the
head pointer for the reversed list. The only
important thing here is that you save the pointer to the next node
before overwriting it.
%tested
\begin{lstlisting}[basicstyle=\footnotesize,numbers=left,breaklines=true,language=C++]
Node* ReverseLinkedList(Node* head) {
  Node* prev = NULL;
  Node* current = head;
  while(current != NULL) {
    Node* tmp = current->next;
    current->next = prev;
    prev = current;
    current = tmp;
  }
  return prev;
}
\end{lstlisting}

\ans{cycle} There are two elegant solutions to this problem. One
solution is that you try to reverse the linked list and one of the two things can happen:
\begin{enumerate}
\itemsep 1pt

\item You reach the null pointer at the end of the
list---this indicates that this was a correctly constructed linked list.
\item You reach the head pointer of the list which indicates that the linked list has a loop.
\end{enumerate}

Of course this operation is destructive,
i.e., you modify your input but you can restore the input by
reversing it again.

Another interesting approach is to have two pointers traverse the linked
list and in every step, you advance the pointers. The first pointer is
advanced by one position and the second one is advanced by two
positions. If you have a correctly constructed linked list, then both
the pointers will end up at the tail of the list. However if you have
a circular linked list then you would be in an infinite loop.
Since the second pointer is traversing the loop twice as fast as the
first, it will often intersect with the first pointer in the loop. If
you find the two pointers intersect, this would indicate the list is circular.

\ans{delete-list}
This is more of a trick question than a conceptual one. Given the pointer to a node, it is
impossible to delete it from the list without modifying its predecessor's
next pointer and the only way to get to the predecessor is to
traverse the list from head. However it is easy to delete the
next node since it just requires modifying the next pointer of the current
node. Now if we copy the value part of the next node to the current node,
this would be equivalent to deleting the current node.

(This question used to be commonly asked but it would be poor practice
to use this solution in real life---for example, a reference
to the successor of the node that was just deleted is now corrupted.)

\ans{bad-bin-search}
At first glance, it would appear that the search function
does a constant amount of work and then recurses on a subarray
that is less than half as big as the array passed in---a
classic  $O(\log n)$ algorithm.

However the array slicing---the construction of the subarray---is potentially
expensive, depending on how it is implemented.
Different languages implement array slicing in different ways: the elements may
be aliased to elements in the original array or they may be copied.
If a copy is being made, this copy takes $\Theta(l)$
time to compute, where $l$ is the length of the array slide.
Therefore the recurrence is $T(n) = \Theta(n) + T(\frac{n}{2})$,
which solves to $T(n) = \Theta(n)$.

The right way to perform binary search,
which avoids the copy, passes integer indexes denoting the range
to perform search on (alternately, a while loop can be used to avoid
recursion).  See Problem~\ref{bin-search} for more details.