• I can't teach recursion to you, but if I can teach you how to teach recursion to kids, maybe we're somewhere.
  • There's a lot of things here that we can use to make up the basics (connecting math induction to recursion)
  • Power of two, tromino, monomino, induction proof, problem. here
  • Recursive story by douglas hofstadter
  • Recursion Fairy (point 1) Fairy
  • Recursion v Iteration and StackOverflow (through fibonacci maybe)
  • google 'recursion'
  • understanding research papers in math (all the way recursively till Euclid)
  • without the Fairy - two mirrors to see what's in the opposite one
  • Towers of Hanoi: There is a story about an Indian temple in Kashi Vishwanath which contains a large room with three time-worn posts in it, surrounded by 64 golden disks. Brahmin priests, acting out the command of an ancient prophecy, have been moving these disks in accordance with the immutable rules of Brahma since that time. According to the legend, when the last move of the puzzle is completed, the world will end (show running recursive code)
  • visualization of ToH here
In [2]:
from typing import List
A: List[int] = [i for i in range(4, 0, -1)]
height: int = len(A) - 1  
B: List[int] = []
C: List[int] = []
In [3]:
def move(n: int, source: List[int], target: List[int], auxiliary: List[int]):
    if n > 0:
        move(n - 1, source, auxiliary, target)
        target.append(source.pop())
        draw_disks(A, B, C, height)
        print() 
        input()
        move(n - 1, auxiliary, target, source)
In [4]:
def draw_disks(A: List[int], B: List[int], C: List[int], position: int, width: int = 2 * int(max(A))):
    if position >= 0:
        valueInA: str = " " if position >= len(A) else create_disk(A[position])
        valueInB: str = " " if position >= len(B) else create_disk(B[position])
        valueInC: str = " " if position >= len(C) else create_disk(C[position])

        print("{0:^{width}}{1:^{width}}{2:^{width}}".format(valueInA, valueInB, valueInC, width=width))

        draw_disks(A, B, C, position - 1, width)
    else:
        print("{0:^{width}}{1:^{width}}{2:^{width}}".format("A", "B", "C", width=width))

Important: The challenge of teaching recursion was attemped using recursion. Parallel "if I could use a fairy to solve a smaller problem, then by {idea}, I'd solve the original problem" to "if I could use a fairy to teach you how to teach recursion, then by {telling you I did so to teach you recursion}, I'd solve the original problem. This recursion was pretty small, with only two layers, but the number of layers is not a problem for us once we have a {base case} and an {idea}.

In [5]:
def create_disk(size: int) -> str:
    if size == 1:
        return "/\\"
    else:
        return "/" + create_disk(size - 1) + "\\"
In [6]:
move(len(A), A, C, B)
                        
  //\\                  
 ///\\\                 
////\\\\   /\           
   A       B       C    


                        
                        
 ///\\\                 
////\\\\   /\     //\\  
   A       B       C    


                        
                        
 ///\\\            /\   
////\\\\          //\\  
   A       B       C    


                        
                        
                   /\   
////\\\\ ///\\\   //\\  
   A       B       C    


                        
                        
   /\                   
////\\\\ ///\\\   //\\  
   A       B       C    


                        
                        
   /\     //\\          
////\\\\ ///\\\         
   A       B       C    


                        
           /\           
          //\\          
////\\\\ ///\\\         
   A       B       C    


                        
           /\           
          //\\          
         ///\\\ ////\\\\
   A       B       C    


                        
                        
          //\\     /\   
         ///\\\ ////\\\\
   A       B       C    


                        
                        
                   /\   
  //\\   ///\\\ ////\\\\
   A       B       C    


                        
                        
   /\                   
  //\\   ///\\\ ////\\\\
   A       B       C    


                        
                        
   /\            ///\\\ 
  //\\          ////\\\\
   A       B       C    


                        
                        
                 ///\\\ 
  //\\     /\   ////\\\\
   A       B       C    


                        
                  //\\  
                 ///\\\ 
           /\   ////\\\\
   A       B       C    


                   /\   
                  //\\  
                 ///\\\ 
                ////\\\\
   A       B       C    


  • example where recursion is bad: Fibonacci (more on this in the DP lecture)
In [7]:
def fibonacci(n: int):
    return fibonacci(n - 1) + fibonacci(n - 2) if n > 1 else 1
In [8]:
fibonacci(42)
Out[8]:
433494437
In [2]:
def factorial(n: int):
    return n * factorial(n - 1) if n > 1 else 1
In [3]:
factorial(270)
Out[3]:
6662114104669102659441021975736134627769448930279862933653218630551948003511087832565415828448678280273978753617211105330479503215391395051285941411676952807468659285639696303194016356426102905293246092874474761645912856695644240665245982604485191327580894984261640512855484243841384335849362695123615035750935706878324574737858489512861092219029899373726694555642915186832331932293272162921969108837665231946747134114678139970333858780786306763062986052579371266852134060032000000000000000000000000000000000000000000000000000000000000000000
In [4]:
# import sys
# sys.getrecursionlimit()
# sys.setrecursionlimit(1)
  • common class exercise: find out what the (default) recursion depth limit is in Python, thus explaining the importance and effect of divide and conquer as a technique and how it is recursive in nature.
  • Why does factorial(42) take so lesser time than fibonacci(42) even if the result is so larger?
In [5]:
factorial(3750)
---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-5-35d9bae71daa> in <module>
----> 1 factorial(3750)

<ipython-input-2-423b17c2de85> in factorial(n)
      1 def factorial(n: int):
----> 2     return n * factorial(n - 1) if n > 1 else 1

... last 1 frames repeated, from the frame below ...

<ipython-input-2-423b17c2de85> in factorial(n)
      1 def factorial(n: int):
----> 2     return n * factorial(n - 1) if n > 1 else 1

RecursionError: maximum recursion depth exceeded in comparison
In [ ]: