S19 LECTURE 21: RECURSION, DIVIDE-AND-CONQUER, Summaries of Design

A recursive definition or a recursive function it is running back in the ... RecursionError: maximum recursion depth exceeded while calling a Python ...

Typology: Summaries

2022/2023

Uploaded on 02/28/2023

anvi
anvi 🇺🇸

4.8

(4)

228 documents

1 / 33

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
15-110 PRINCIPLES OF COMPUTING S19
TEACHER:
GIANNI A. DICARO
LECTURE 21:
RECURSION, DIVIDE-AND-CONQUER
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21

Partial preview of the text

Download S19 LECTURE 21: RECURSION, DIVIDE-AND-CONQUER and more Summaries Design in PDF only on Docsity!

15 - 110 PRINCIPLES OF COMPUTING – S

TEACHER:

GIANNI A. DI CARO

LECTURE 21:

RECURSION, DIVIDE-AND-CONQUER

Recursion

Informal definitions of recursion:

§ A function that makes a call to itself

§ More in general, a thing defined in terms of itself or of its type

Ø This might lead to an infinite regress!

ü Properly defined mathematical recursion solution is never infinite: the recursive calls

to itself eventually reach an end, when some minimal input (base case) is reached

A matryoshka doll is a wooden Russian doll

that contains a smaller sized matryoshka doll

To understand recursion you must

first understand recursion!”

J

The adjective recursive originates from the Latin verb recurrere , which means to

run back. A recursive definition or a recursive function it is "running back" in the

sense of returning to itself (possibly with a simpler instance)

A concrete example: computing the factorial

!! =! ×! − 1 ×! − 2 × ⋯× 1 , ∀! ∈ ℕ,! > 1

§ Mathematical definition (e.g., 5! = 5 × 4 × 3 × 2 × 1 = 120 )

§ We can rewrite it as:

!! =! ×! − 1! ∀! ∈ ℕ,! > 1

§ In a more general form:

Recursive definition of the factorial function

A concrete example: computing the factorial

def factorial_recursive(n):

if n == 1 or n == 0:

return 1

else:

return n * factorial_recursive(n-1)

Recursive definition of the factorial function !:

Base case (s): it doesn’t require recursion, where the recursive calls of! stop

Recursive case (s): the function calls itself, with a simpler instance / smaller value

print(factorial_recursive(5))

120

Let’s look inside the recursive process …

n = 5

if 5 == 1:

return 1

else:

return 5 *

if 4 == 1:

return 1

else:

return 4 * if 3 == 1:

return 1

else:

return 3 * if 2 == 1:

return 1

else:

return 2 *

if 1 == 1:

return 1

§ Let’s consider the case! = 5 and let’s expand the function calls:

False

False

False

False

True

Recursive calls end:

the base case , that

does not require

recursion, is reached!

f(5)

f(4)

f(3)

f(2)

f(1)

f(5)

6

n = 5

1

f(5)

6

n = 5

1

f(4)

5

n = 4

1

f(5)

6

n = 5

1

f(4)

5

n = 4

1

n = 3

1

f(3)

5

f(5)

6

n = 5

1

f(4)

5

n = 4

1

n = 3

1

f(3)

5

f(2)

5

n = 2

1

f(5)

6

n = 5

1

f(4)

5

n = 4

1

n = 3

1

f(3)

5

f(2)

5

n = 2

1

f(1)

5

n = 1

1

Run-time

memory stack

Let’s look inside the recursive process …

return 4 * 6

return 3 * 2

return 2 * 1

if 1 == 1:

return 1

return 5 * 24

120

f(5)

6

n = 5

1

f(4)

5

n = 3

1

f(2)

5

f(1)

5

n = 1

1

n = 2

1

f(3)

5

n = 4

1

Each function return pops out a call

frame from the run-time memory stack

f(4)

5

n = 3

1

f(2)

5

n = 2

1

f(3)

5

n = 4

1

×
×
×
×

n = 3

1

f(3)

5

×
×
×
×
×
×

f(5)

6

n = 5

1

f(5)

6

n = 5

1

f(4)

5

n = 4

1

f(5)

6

n = 5

1

f(4)

5

n = 4

1

f(5)

6

n = 5

1

ü Now all the returns can be processed backward , popping out

the functions’ call frames from the run-time memory stack

Iteration vs. Recursion for the factorial

§ We can easily implement the factorial

function using iteration

def factorial_it(n):

v = 1

for i in range(2, n+1):

v *= i

return v

o Is the iteration-based implementation faster

than the recursion-based one?

o Let’s check their running times for! = 0 , ⋯ , 40

Iteration seems to be a clear winner,

in this specific case ….

Comparing running times: a helper function

§ The previous results were produced with the custom helper function below, quite useful, give it a look!

Why do we need recursion?

§ Often, we can use iteration instead of recursion, but recursion, when well designed, can be:

§ More elegant and compact

§ More efficient, computationally

§ Some problems are naturally defined in recursive terms (recurrence equations, iterated maps)

§ Some problems might actually need recursion!

§ However, it may be true that we need some “practice” to learn to think recursively …

§ Trying to enroll the recursive process in our mind is not the way to go , it wouldn’t work, it’d be

just frustrating. We can do this easily for iteration, but the almost unbounded regress process

happening in recursion would be confusing for our minds

§ Recursion is tightly related to the principle of mathematical induction , that can help to think

recursively …

Mathematical induction

ü Mathematical induction is a mathematical proof technique: given a certain property !(#), prove that it

holds for all integer numbers, # = 0 , 1 , 2 , ⋯

§ Proving by induction requires to prove the truth of two cases:

§ Base case , that proves that the property is true for # = 0 (or # = 1 ), that is for the first integer

§ Induction case , that proves that, if the property holds for a generic # ( inductive assumption, the

validity for # becomes a fact ), it also holds for the next natural number, # + 1

§ If we can prove that both the induction and the base case hold, then we can assert that the property

holds for any integer

§ Induction is a way to perform inference and is at the foundation of the correctness of all computer programs!

From mathematical induction to recursive function definitions

Ø When programming recursively, think inductively!

ü Define a recursive (inductive) case, that recursively relates the function! for a generic input " to the value

of the function for a downsized input "# (e.g., "# = " − 1 )

o When you write the recursive relation, assume / trust that the value returned by !( "#) is correct (this is

the inductive thinking!) That is, it does the job of correctly computing the function for the input "#.

o E.g., in the case of the factorial, we have assumed that !(* − 1 ) does return the factorial of * − 1 and

we have used it for defining a recursive relation that computes !(*)

ü Define base (non-recursive) cases for the input ", that return the value of the function for the minimal

admitted inputs ", such that when the input regresses to these values, the recursive calls are interrupted

and the process stops returning the result by combining the results from the individual recursive calls

o E.g., in the case of the factorial, the minimal input is for * = 1 , that we know how to compute without

recursion, the final result is obtained by multiplying the results from each individual function call

Another example: exponentiation

§ Problem: compute! ", $ = "

&

recursively, where $ is an integer

§ Base case:! ", 0 = 1

§ Recursive case:! ", $ = " ) !(", $ − 1 )

def exponentiate(x, n):

if n == 0:

return 1

else:

return x * exponentiate(x, n - 1)

exponentiate(2, 7)

def exponentiate(x, n):

if n == 0:

return 1

if x == 0:

return 0:

else:

return x * exponentiate(x, n - 1)

Optimized version for the special case when " = 0

Fibonacci sequence: naturally defined in a recursive way

Recurrence equations, two base cases:

§ Let’s start with a pair of rabbits, one male, one female. Rabbits are able to mate at the age of one month so

that at the end of its second month (% = 2 ) a female can produce another pair of rabbits. Suppose that our

rabbits never die and that the female always produces one new pair (one male, one female) every

month from the second month on.

Ø How many pairs f rabbits will there be after % months?

Ø Instance of a discrete-time dynamical system : the state of the system (the number of rabbit pairs)

evolves over time under a growth law expressed by recurrence equations

Fibonacci sequence: defined in a recursive way

def fibonacci(n):

if n == 0:

return 0

elif n == 1:

return 1

else:

return fibonacci(n-1) + fibonacci(n-2)

for i in range(20):

print(fibonacci(i))

0 1 1 2 3 5 8

13

21

34

55

89

144

233

377

610

987

1597

2584

4181

o Fibonacci numbers are “pervasive” in natural phenomena

o They capture growth modalities and ratios in nature

o http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibnat.html