Setting up and solving recurrences

Simple linear recurrences

Example: Sum of items in list

An easy first example.

nat sum(List x)
{
   if (x.empty?)
      return 0
   else
      return x.first + sum(x.rest)
}

What is the appropriate size measure? — length of x.

T(n) = "steps" to compute sum(x), where n=|x|

To solve, unroll into a summation: T(n) = &sumi=0…n 1 = n+1 = O(n)

Example: Some similar sums

To solve, unroll into a summation: T(n) = &sumi=0…n i = n(n+1)/2 = O(n2)

To solve, unroll into a summation: T(n) = &sumi=0…n 2i = 2 &sumi=0…n i = 2n(n+1)/2 = O(n2)

Example: Merge

List merge(List x, List y)
{
   if (x.empty?)
     return y
   if (x.first < y.first)
     return cons(x.first, merge(x.rest, y))
   else
     return cons(y.first, merge(x, y.rest))
}

First attempt at a recurrence:

A bit tricky to solve. (Although we could eventually find that T(m,n) = O(m+n), not surprisingly.)

For this and all other examples, could guess the closed form solution (or its bound), and prove the guess correct inductively. However, we're currently interested in finding such closed forms without guessing.

Second attempt: A unary T(n) would be easier to solve. Does it make sense here? What is appropriate measure of input size?

To solve, assume worst case, which then looks just like previous example.

When is this unrolling into a sum easy? General form:

Assuming the base case happens when nk, for some constant k, which is almost always the case, can simply omit writing the base case.

Divide-and-Conquer-style Recurrences

Example: Mergesort

List mergesort(List x)
{
   if (x.empty? or x.rest.empty?)
      return x
   else
      y,z = split x into halves
      return merge(mergesort(y), mergesort(z))
}

If |x| isn't a power of two, eventually get an uneven split. Let's temporarily ignore this.

To solve…

Could have written the following

Gives, within a constant factor, the same result.

What if n isn't a power of two?

Rather than solving directly, observe that more input never takes less time.

This idea can be generalized. Rule of thumb: floors and ceilings in recurrences can usually be safely ignored.

Aside: O(n log n) vs. O(n log2 n)

The logarithm base doesn't matter (if constant): O(log2 n) = O(log4 n) = O(log10 n) = …

Need to prove this.

loga n / logb n = ? — loga b — a constant

Asymptotes ignore constant factors, so can ignore constant bases.

Example: Binary Search

Solve by unrolling.

Master Theorem (simplified version)

For f(n) = af(n/b) + cnd,

We're skipping the proof, but it follows our examples. Unroll the recurrences into a sum, and solve. Each case allows the sum to be bounded in a different way.

Apply to mergesort and binary search.

(The full Master Theorem generalizes the cnd part. See COMP 482 for the general form and proof.)

Other linear recurrences

Example: Breaking an n-digit password

Closed form: T(n) = 10n

Example: Fibonacci

Previously, proved Fn = (an - bn)/√5, where a = (1+√5)/2, b = (1-√5)/2, but how to derive that?

Try different initial conditions, and see how much series varies.

More generally…

Approach to recurrences of the form f(n) = bf(n-1) + cf(n-2):

  1. Guess a sol'n of the form f(n)=αn.

    What is α? Well, we get α2 - b⋅α - c = 0 (the characteristic polynomial).

    (Note: If started with f(n-3) term, we'd have a cubic characteristic polynomial, etc.)

  2. Compute solutions α1, α2 to the characteristic polynomial. Here, use the quadratic equation.

    Verify these answers satisfy the recurrence, temporarily ignoring the initial conditions.

  3. But what about the initial conditions? Hmm, don't work.

    Sol'ns to the recurrence relation (w/o the initial conditions) are closed under (a) sums, (b) constant factors. If α1n and α2n are sol'ns, then so are j⋅α1n + k⋅α2n, for any j,k ∈ ℝ.

    (That is, "a linear combo. of two sol'ns is also a sol'n", or equivalently, "the set of solutions form a vector space".)

    Solve for j,k.

  4. Are we done? Are there more solutions that aren't of the form αn? At this point, we ask the mathematicians, and trust us when they give us the answer we want: yes, this is all solutions.

This technique generalizes:

  1. Guess an exponential, get a polynomial of degree n,
  2. Solve roots α1, …, αn.
  3. A linear combination of those α's makes up all the possible answers.
    Caveat: if αi occurs with multiplicity m, then you also include αin, n⋅αin, n2⋅αin, …, nm⋅αin.