1 of 79

Lecture 16

Recursion

2 of 79

How would you code this problem? (regular way)

Method that returns TRUE if any element in the array is odd

3 of 79

Method that returns TRUE if any element in the array is odd

def anyOdd(lst):

for num in lst:

if num % 2 == 1:

return True

return False

anyOdd([2, 4, 6, 8])

4 of 79

Method that returns TRUE if any element in the array is odd

def anyOdd(lst):

for num in lst:

if num % 2 == 1:

return True

return False

anyOdd([2, 4, 6, 8])

We are going to solve this problem using recursion!

5 of 79

What is the Recursion?

  • Algorithmically: a way to design solutions to problems by divide-and-conquer method
    • Reduce a problem to simpler version of the same problem

6 of 79

Recursion: (1904) ->

https://en.wikipedia.org/wiki/Recursion

Recursion occurs when a thing is

defined in terms of itself

Droste effect

7 of 79

What is the Recursion?

  • Algorithmically: a way to design solutions to problems by divide-and-conquer method or decrease-and-conquer method
    • Reduce a problem to simpler version of the same problem

  • Semantically: a programming technique where a function calls itself
    • In programming, the goal is NOT to have an infinite recursion
    • Must have at least 1 base case that are easy to solve
    • Must solve the same problem on some other input with the goal of simplifying the larger problem input.

8 of 79

Recursion: Why?

  • Why do you use it?
    • Perfect for problems where there is an obvious answer for some small problem and all larger problems build from smaller problems.

  • There are iterative (loop based) solutions for every problem solvable with recursion. Use whichever is simpler
    • Although there may be performance implications of each

9 of 79

Recursive Functions

Definition: A function is called recursive if the body of that function calls itself

def print_me_again(x):

if (x == 0):

return

print(x)

print_me_again(x-1)

10 of 79

Get to a smaller problem

def anyOdd_2(lst):

if (lst[0] % 2 == 1):

return True

else:

smaller = lst[1:]

return anyOdd_2(smaller)

anyOdd_2([0, 4, 6, 8])

Will this code work?

A: Yes, always

B: Never

C: Sometimes

D: I have no idea

11 of 79

Fixed. Trace it

def anyOdd_3(lst):

if (len(lst) == 0): #added the base case

return False

elif (lst[0] % 2 == 1):

return True

else:

smaller = lst[1:]

return anyOdd_3(smaller)

anyOdd_3([2, 4, 6, 8])

12 of 79

Question

def hello(x):

print(x)

hello(x-1)

What happens if we call

hello(0)

A: Compiler error

B: 0

C: 0 -1 -2 -3 -4 …

D: 0 -1 -2 -3 -4….until crash

E: None of the above

Each number is on a new line ----->

13 of 79

Trace it + Demo

def hello(x):

print(x)

hello(x-1)

hello(0)

14 of 79

Recursion: Base case!

  • Know when to stop! Known as the base case
    • When the array only has one element (or no elements)
    • Solution to a small problem.

15 of 79

Question (X! = 1 * 2 * 3*...*X)

def fact(x):

if (x == 0):

_____________

else:

_________________

print(fact(5))

Fill in blanks (assume x is not negative)

A: return 0 fact(x)

B: return 1 return x * fact(x-1)

C: return 1 return (x-1) * fact(x-1)

D: return 0 return x * fact(x-1)

E: None of the above

16 of 79

Demo (with PythonTutor)

def fact(x):

if (x == 0):

return 1

else:

return x*fact(x-1)

print(fact(5))

17 of 79

Reminder/Announcements

  • Mic
  • Exam info (brief):
    • Topics (up to, including) recursion.
    • Pen/Paper. No cheat sheets. (you will have one for the Final)
    • Two rooms per session:
      • This one + PCYNH 106 (noon)
      • This one + CENTR 119 (1pm)
      • I will create a seating chart by Wed.
  • How to study:
    • Solve the practice (recent) exams on paper.
      • Print it, time yourself to check the progress.
      • Generate problems to work on using charGPT and solve them on paper, then check your solution.

18 of 79

Steps to design a recursive algorithm

  • Base case:
    • For small values of n, it can be solved directly
  • Recursive case(s)
    • Smaller version of the same problem

Algorithmic steps

  • Identify the base case and provide a solution for it
  • Reduce the problem to smaller version of itself
  • Move towards the base case using smaller input versions

19 of 79

Let’s practice

Write a recursive method that calculates the product of a and b, where a and b are inputs that method. (a, b are positive ints)

Iterative solution:

def mult_it(a, b):

result = 0

while b > 0:

result = result + a

b = b - 1

return result

Recursive solution:

def mult_rec(a, b):

20 of 79

Let’s practice + env. diagram

Write a recursive method that calculates the product of a and b where a and b are positive ints.

Iterative solution:

def mult_it(a, b):

result = 0

while b>0:

result = result + a

b = b - 1

return result

Recursive solution:

def mult_rec(a, b):

if b==1:

return a

else:

return a+mult_rec(a, b-1)

21 of 79

Let’s practice

Given a non-negative int n, return the count of the occurrences of 9 as a digit.

  • Note that mod (%) by 10 yields the rightmost digit (129 % 10 is 9)

  • divide (//) by 10 removes the rightmost digit (129 // 10 is 12)

  • No loops

def count_9s (number):

"""

>>> count_9s(919)

2

>>> count_9s(7)

0

>>> count_9s(129)

1

"""

# Base case. Think how many do you need.

22 of 79

Let’s practice

Given a non-negative int n, return the count of the occurrences of 9 as a digit.

  • Note that mod (%) by 10 yields the rightmost digit (129 % 10 is 9)

  • divide (//) by 10 removes the rightmost digit (129 // 10 is 12)

  • No loops

def count_9s (number):

"""

>>> count_9s(919)

2

>>> count_9s(7)

0

>>> count_9s(129)

1

"""

# Base case. Think how many do you need.

if number == 9:

return 1

elif number < 9:

return 0

else:

What is your recursive step?

23 of 79

Let’s practice

Given a non-negative int n, return the count of the occurrences of 9 as a digit.

  • Note that mod (%) by 10 yields the rightmost digit (129 % 10 is 9)

  • divide (//) by 10 removes the rightmost digit (129 // 10 is 12)

  • No loops

def count_9s (number):

"""

>>> count_9s(919)

2

>>> count_9s(7)

0

>>> count_9s(129)

1

"""

# Base case. Think how many do you need.

if number == 9:

return 1

elif number < 9:

return 0

else:

rem = number % 10

smaller = number //10

if (rem == 9):

return 1 + count_9s(smaller)

else:

return 0 + count_9s(smaller)

24 of 79

Let’s practice

  • Do not need 2 bases cases for this

problem

def count_9s (number):

"""

>>> count_9s(919)

2

>>> count_9s(7)

0

>>> count_9s(129)

1

"""

if number < 9:

return 0

else:

rem = number % 10

smaller = number //10

if (rem == 9):

return 1 + count_9s(smaller)

else:

return 0 + count_9s(smaller)

25 of 79

Check if a given string is a palindrome

def is_palindrome (input):

"""

>>> is_palindrome ('123321')

True

>>> is_palindrome('34554')

False

>>> is_palindrome('12321')

True

"""

# Base case

26 of 79

Check if a given string is a palindrome

def is_palindrome (input):

"""

>>> is_palindrome ('123321')

True

>>> is_palindrome('34554')

False

>>> is_palindrome('12321')

True

"""

# Base case

if len(input) == 0:

return True

27 of 79

Check if a given string is a palindrome

def is_palindrome (input):

"""

>>> is_palindrome ('123321')

True

>>> is_palindrome('34554')

False

>>> is_palindrome('12321')

True

"""

# Base case

if len(input) == 0:

return True

if len(input) == 1:

return True

# recursive step

28 of 79

Check if a given string is a palindrome

def is_palindrome (input):

"""

>>> is_palindrome ('123321')

True

>>> is_palindrome('34554')

False

>>> is_palindrome('12321')

True

"""

# Base case

if len(input) == 0:

return True

if len(input) == 1:

return True

# recursive step

else:

if input[0] == input[-1]:

?

29 of 79

Check if a given string is a palindrome

def is_palindrome (input):

"""

>>> is_palindrome ('123321')

True

>>> is_palindrome('34554')

False

>>> is_palindrome('12321')

True

"""

# Base case

if len(input) == 0:

return True

if len(input) == 1:

return True

# recursive step

else:

if input[0] == input[-1]:

return is_palindrome(input[1:-1])

else:

return False

30 of 79

Given a string, compute recursively the number of x characters in the string

def count_x (input):

"""

>>> count_x("xxhixx")

4

>>> count_x("xhixhix")

3

>>> count_x("hi")

0

"""

# base case?

31 of 79

Given a string, compute recursively the number of x characters in the string

def count_x (input):

"""

>>> count_x("xxhixx")

4

>>> count_x("xhixhix")

3

>>> count_x("hi")

0

"""

# base case?

if input == '': # if input is an empty string

return 0

else:

32 of 79

Given a string, compute recursively the number of x characters in the string

def count_x (input):

"""

>>> count_x("xxhixx")

4

>>> count_x("xhixhix")

3

>>> count_x("hi")

0

"""

# base case?

if input == '': # if input is an empty string

return 0

else:

if input[0] == 'x':

return 1 + count_x (input[1:])

33 of 79

Given a string, compute recursively the number of x characters in the string

def count_x (input):

"""

>>> count_x("xxhixx")

4

>>> count_x("xhixhix")

3

>>> count_x("hi")

0

"""

# base case?

counter = 0

if input == '': # if input is an empty string

return 0

else:

if input[0] == 'x':

return 1 + count_x (input[1:])

else:

return 0 + count_x (input[1:])

34 of 79

Trace it. Demo

def magic(x):

if (x>1):

magic(x-1)

print(x)

return None

magic(5)

What is the output?

A: 5

B: 5 4 3 2 1 #new line for each number

C: 1 2 3 4 5 #new line for each number

D: 1

E: Error or Infinite recursion

35 of 79

Trace it. Demo

def magic(x):

if (x>1):

print(x)

magic(x-1)

magic(5)

What is the output?

A: 5

B: 5 4 3 2 #new line for each number

C: 5 4 3 2 1 #new line for each number

D: 1 2 3 4 5 #new line for each number

E: 1

F: Error or Infinite recursion

36 of 79

Check point 1: print All odd numbers

def func(lst):

```

>>> func([3, 1, 2, 0])

3

1

```

if (len(lst) == 0):

return

else:

if (lst[0] % 2 == 1):

print(lst[0])

func(lst[1:])

else:

func(lst[1:])

Correct solution?

A: Yes for all inputs

B: Yes but for some inputs

C: Does not work for any input

37 of 79

Check point 2

def magic(lst):

if len(lst) == 0:

return []

return [lst[-1]] + magic(lst[:-1])

magic([1, 2, 3])

Purpose of the code?

A: Swap first and last elements in the list

B: Create a palindrome

C: Reverse a given list

D: Reverse the first half of the list

E: To confuse me

38 of 79

def magic(lst):

if len(lst) == 0:

return [ ]

return [lst[-1]] + magic(lst[:-1])

magic([1, 2, 3])

39 of 79

Practice if we have time. Classic question

Sum of Digits:

Create a recursive function that calculates the sum of the digits of a positive integer n.

For example, if n =123, the sum would be 1+2+3=6.

40 of 79

Practice if we have time.

41 of 79

Optional

42 of 79

Mutual Recursion

Tree Recursion

43 of 79

Types of Recursions

Direct Recursion

def function(x):

function(x-1)

44 of 79

Types of Recursions

Direct Recursion

def function(x):

function(x-1)

Indirect (or Mutual) Recursion

def function_a(x):

...

function_b(x-1)

...

def function_b(y):

...

function_a(y-2)

...

45 of 79

Classic example: even - odd

  • a number is even if it is one more than an odd number
  • a number is odd if it is one more than an even number
  • 0 is even

46 of 79

Classic example: even - odd

  • a number is even if it is one more than an odd number
  • a number is odd if it is one more than an even number
  • 0 is even

def is_even(n):

47 of 79

Classic example: even - odd

  • a number is even if it is one more than an odd number
  • a number is odd if it is one more than an even number
  • 0 is even

def is_even(n):

if n == 0:

return True

48 of 79

Classic example: even - odd

  • a number is even if it is one more than an odd number
  • a number is odd if it is one more than an even number
  • 0 is even

def is_even(n):

if n == 0:

return True

else:

return is_odd(n-1)

49 of 79

Classic example: even - odd

  • a number is even if it is one more than an odd number
  • a number is odd if it is one more than an even number
  • 0 is even

def is_even(n):

if n == 0:

return True

else:

return is_odd(n-1)

50 of 79

Classic example: even - odd

  • a number is even if it is one more than an odd number
  • a number is odd if it is one more than an even number
  • 0 is even

def is_even(n):

if n == 0:

return True

else:

return is_odd(n-1)

def is_odd(n):

if n == 0:

51 of 79

Classic example: even - odd

  • a number is even if it is one more than an odd number
  • a number is odd if it is one more than an even number
  • 0 is even

def is_even(n):

if n == 0:

return True

else:

return is_odd(n-1)

def is_odd(n):

if n == 0:

return False

else:

52 of 79

Classic example: even - odd

  • a number is even if it is one more than an odd number
  • a number is odd if it is one more than an even number
  • 0 is even

def is_even(n):

if n == 0:

return True

else:

return is_odd(n-1)

def is_odd(n):

if n == 0:

return False

else:

return is_even(n-1)

53 of 79

Let’s practice

Write two methods that calculate the following functions that are defined in terms of each other:

54 of 79

Let’s practice

Write two methods that calculate the following functions that are defined in terms of each other:

def A(x):

if x<= 1:

return 1

else:

return B(x+2)

def B(x):

return A(x-3) + 4

55 of 79

Tree recursion

Credit: J. DeNero

56 of 79

Tree recursion

Tree-shaped processes arise whenever executing the body of a recursive function makes more than one recursive call

57 of 79

Tree recursion

Tree-shaped processes arise whenever executing the body of a recursive function makes more than one recursive call

58 of 79

Tree recursion

Tree-shaped processes arise whenever executing the body of a recursive function makes more than one recursive call

59 of 79

Tree structure

60 of 79

Tree structure

61 of 79

Tree structure

62 of 79

Tree structure

63 of 79

Tree structure

64 of 79

Tree structure

65 of 79

Tree structure

66 of 79

Tree structure

67 of 79

Tree structure

68 of 79

Tree structure

69 of 79

Tree structure

70 of 79

Tree structure

71 of 79

Tree structure

72 of 79

Tree structure

73 of 79

Tree structure

74 of 79

Tree structure

75 of 79

Tree structure

76 of 79

Tree structure

77 of 79

Tree structure

78 of 79

Tree structure

79 of 79

Tree structure