Algorithmic Design

  • To design sophisticated algorithms, you must get really sophisticated at _communicating _about algorithms.
    • Listening and Speaking
  • How do we formally and rigorously discuss the correctness of code?

Divide and Conquer

  • There are two main styles of D&C algorithms, both of which should be familiar
    • Mergesort
      • Divide into two subproblems
      • Recursively solve both subproblems
      • Cleverly merge the solutions
    • Binary Search
      • Cleverly split into 2 subproblems
      • Recursively solve 1 subproblem
  • Both styles are clever in different places. Mergesort style is clever about merging solutions, only after both subproblems have been solved
  • Binary Search style is clever about picking which subproblem to solve, and then simply making recursive calls
  • These are the styles of algorithms that we will start exploring first

Intuition -> Formalization

  • Learn how to formalize concepts that you already are familiar/intuitive so that you can analyze new and unique algorithms.
  • This formalization is a form of communication about the very nature of algorithms. How better to convince others about the nature and correctness of your algorithm if not through formalized, provable. communication.
  • Visualize a spectrum of communication, with intuition on the left, and formalization on the right. Learn how to intentionally shift your method of communication from one direction to the other, depending on your context and audience.
  • How would you argue the correctness of Mergesort?
    • A feature of Mergesort is that it is fast, but analyzing the speed of an algorithm is different from analyzing its correctness
  • Induction is how we can cleanly and compactly analyze long sequences of operations.
    • Previously, induction was used to argue claims about numbers, formulas, and relations.
    • How can induction be used to prove things about code?
    • Code execution is a dynamic process. Variables change at every step.
    • How to cleanly capture this with an Inductive Hypothesis, and perform an Inductive Step to draw a conclusion about the correctness?
  • A standard Inductive Hypothesis for recursive code is:
    • “my function is correct on all smaller inputs”
    • Where exactly what is meant by “correct” and “smaller” will depend on the specifics of the code
    • e.g., Mergesort Inductive Hypothesis:
      • “Mergesort correctly sorts all arrays of length < n”

Code Example: Iterative Preview

Func(n):
x <- 5
for i <- 1 to n
  x <- x + 2
output x
  • How can induction show that outputs ?
  • This code is very similar to the recursive definition of a sequence
  • Let ; for all ;
  • Inductive Hypothesis: ”
  • Base Case: choose ; ; ;
  • Inductive Step:
    • (by definition of the recurrence for )
    • (substitute the induction hypothesis for )
    • (simple algebra)
  • Thus, the Inductive Hypothesis has been shown for . Since the base case and IH have for implies the IH for , it is concluded (by induction) that the IH is true for all .
  • Induction is a subtle but powerful tool that should become very comfortable
  • Performing induction on code is requires choosing a clever Inductive Hypothesis
  • The Induction Hypothesis should capture the state of your code at key moments
  • To understand how to induct on this code, look first an example on how to induct on the recursive version

Induction on a Positional Game

LAVA
LAVA
CMonsterT
LAVA
LAVA
  • Imagine you are playing a game (picture above), where you, the character, denoted as , are trying to reach a treasure, located at position
  • Your character can move up/down/left/right. You may not step on any locations occupied by Lava or a monster.
  • How can it be proven that the player cannot reach the treasure?
    • Show that for any time , the character cannot be at the treasure location at time
  • What is a good Inductive Hypothesis for this scenario?
    • One idea, use a statement to describe the state of the game
    • IH: “At time , the character is not at location
    • The base case is true, since at time , the character starts far from
    • However, this Inductive Hypothesis is not useful for the Inductive Step.
    • The character could be located anywhere except location at time , even directly adjacent to
    • There is no way to prove is not at at time
  • Take a step back and reevaluate, determine a state of the game that is always true at time and allows for an inductive step to be made at time
    • IH: “At time , is to the left of
    • The base case, once again is true at time
    • Inductive Step: Assume by the Inductive Hypothesis that is to the left of the bridge at time . Next, can move up/down/left/right, but cannot step on the bridge since is there, and cannot get to the right of the bridge without first stepping on the bridge. Therefore, at time , the character must still be to the left of the bridge
    • By Induction, at all times , must be to the left of the bridge, and since the treasure is at which is to the right of the bridge, will never reach the treasure

Revisiting Code Example: Recursive Preview

Func2(n):
if n == 0, return 5
else, return Func(n-1) + 2
  • For recursive code, the standard Inductive Hypothesis is:
    • ” my function is correct on all smaller inputs”
    • what is meant by “correct” and “smaller” depends on the specific context of the code
  • For the recursive example , induction will be performed on , and the Inductive Hypothesis will be something like:
    • “for all , returns
  • The proof of induction here is similar to the mathematical proof from above

Finish Iterative Code Example

Func(n):
x <- 5
for i <- 1 to n
  x <- x + 2
output x
  • Proving the correctness of this original “for” loop code in , it is tempting to say “clearly and are the same code, thus I will analyze instead of

  • DO NOT DO THIS

  • When asked to analyze a particular piece of code, it is crucial that the code is analyzed EXACTLY as-is, instead of analyzing a different piece of code and hoping it is “close enough”

  • “reductions”, a formal method for relating one piece of code to another, will be discussed later in the course

  • To prove the correctness of , you can construct a complicated (induction) proof analyzing the outputs of ; but it will be much easier to analyze the outputs of instead of this process

  • TLDR: Analyze the code given, and refer to the particular lines of the code in the analysis

  • The challenge for proving the correctness of is to use intuition about (which may be aided by the knowledge from ), to come up with an Inductive Hypothesis that “tells the story” of the “for” loop in

  • In this iterative example, perform induction on the variable , since is the index variable of the “for” loop

  • Tell the story of the code by describing the state of the variables (in this case, ) at each iteration of the “for” loop

  • The goal of the Inductive Hypothesis is to describe the state of things at the ‘th iteration of the “for” loop, in enough detail to use the IH to understand the result of the ‘st iteration and draw a conclusion about the overall behavior of the code after the entire “for” loop as ended

  • What is the value of after the ‘th loop iteration?

  • After some though, it can be guessed that

  • Inductive Hypothesis: “After the ‘th iteration of the “for” loop, will equal

    • this can be proven using induction
  • As a base case, choose , which might seem weird because there is not ‘th iteration of the “for” loop

    • However, this can be interpreted as “what is the value of before the loop begins, for which
    • The base case is true
  • Now, perform the Inductive Step.

    • Suppose the IH is true for a given , show the IH is true for
    • Because IH is true for , this means that after the ‘th iteration of the “for” loop,
    • Now, the ‘st iteration of the “for” loop will execute next, which will execute add to the value of
    • After the ‘st iteration, , which equals , proving the Inductive Step
  • Plugging in , it can be shown that after all iterations of the “for” loop,

  • Thus, the value of will be the output of the program, showing the code is correct

  • It is necessary to perform “clean up” after induction is complete, after all the code doesn’t end with the “for” loop

  • It is a good sign if the structure of the proof mirrors the structure of the code, touching on every line of the code

  • Keep this template in mind when things get more complicated later on