0% found this document useful (0 votes)
32 views37 pages

L18 19 20 Recursion

Uploaded by

gaurav200820ydv
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views37 pages

L18 19 20 Recursion

Uploaded by

gaurav200820ydv
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 37

Recursion

Md Shad Akhtar
IIIT-Delhi
Functions - Recap
● Functions are defined with def <fn-name> (<parms>):

● A defined function can be called from main or another function


● Calling a function - arguments are passed, which are assigned to parameters -
which can be used in function body

● Variables defined / used in a function are local variables by default


● Global vars can be used by explicitly declaring it.

● Functions provide a clean abstraction, which allows a large computational problem to be


broken into smaller computational problems for which functions can be written

● The main program becomes more of a coordinator - with much of the work being done
in functions
Functions - Use
● What would be the output of following two codes segments?

def f1(x): def f1(x):


return f2(x) return f2(x)

def f2(y): print(f1(2))


return y*y*y
def f2(y):
print(f1(2)) return y*y*y

Error: name 'f2' is not defined

f2 is called before its definition.

Not an issue though, f1() will be called only after


definition of f2() !!!
Functions - Use
● When a function definition is encountered, the interpreter just records some
information about it
● Function body is executed only when the function is called
● A function must be declared before it can be used

● But body of a function can call another function declared below it - as body
executed only when function called (as call will originate from main - all functions
declared before main would have been seen by the interpreter)
Recursion
● Recursive definition is one where the defined term is used in the definition
● A function calls itself.
● A recursive algorithm uses itself to solve one or more smaller identical problems.
● All recursive definitions must have:
● Base case: Solution is know – without recursion (e.g. empty list).
● Recursive part: Problem is expressed as a smaller version of the problem itself.
Recursive algorithm
● Searching for a word in a dictionary, i.e., search(‘program’, D) – assume you don’t have any
markers/index
● Based on the first character of the word, we make a guess and goto to some page in the middle.
● If words on the opened page have the first character greater than the first character of the
queried word in alphabetical order
● Query word must be before that opened page.
● Make a new search only on the left side of the dictionary, i.e., search(‘program’, DL),
where DL ⊆ D
● Same problem but with a smaller dictionary or narrower search space.
● If words on the opened page have the first character lesser than the first character of the
queried word in alphabetical order
● Query word must be after that opened page.
● Make a new search only on the right side of the dictionary, i.e., search(‘program’,
DR), where DR ⊆ D
● Same problem but with a smaller dictionary or narrower search space.
● Repeat the process until we find the desired word or search space becomes empty.
Recursion in Python functions
● Python allows recursive functions, i.e., a function can call itself
● Not all languages allowed recursive functions
● Most modern programming languages allow it

● Allowing recursive functions requires care in underlying implementation

● The base case must always be reached

● Every recursive call should bring the function call closer to the base case.

● If the recursive part is such that the base case will not be reached - then we have
infinite recursion (the program will never end).
Recursion - Example
if x = 0,

Breaking the problem into smaller but similar problems


0, f(x = 4) 4
f(x) = +
1+f(x - 1) if x > 0
1 f(x = 3) 3

Constructing the solution


+
def f(x):
1 f(x = 2) 2
if x==0: # base case
return 0 +

else: 1 f(x = 1) 1
return 1 + f(x-1) +

1 f(x = 0) 0

Conquering the smaller problem at each step


Factorial of a number
n! = n * n-1 * n-2 * … * 2 * 1
⇒ n * (n-1)!
⇒ n * (n-1) * (n-2)!
⇒ ...

Factorial of a number is recursive by definition.


● Factorial of a number is defined by multiplying the given number (n) with factorial of n-1.
● Until??
○ (n-1) becomes 1
Base case
Recursive part
Factorial of a number
def factorial(n): def factorial(n):
if n == 1: #base case if n <= 1: #base case
return 1 return 1
else: else:
return (n * factorial(n-1)) return (n * factorial(n-1))
n = 4 n = 4
print(factorial(n)) print(factorial(n))
What about 0!?? What about n < 0 ??

● Not defined.
● Remember end-user is not so intelligent, if this happens,
you (programmer) have to bear all the blame!
○ Checks must be there.
Factorial of a number
def factorial(n):
factorial(n = 4) 24
if n < 0: #invalid case
*
print(‘Not defined’)
return 0 4 factorial(n = 3) 6
if n <= 1: #base case *
return 1
else: 3 factorial(n = 2) 2

return (n * factorial(n-1)) *
n = 4
2 factorial(n = 1) 1
print(factorial(n))
Execution of recursive functions
● Recursive call is similar to a function calling another function - a recursive call is treated as a regular fn call
● When a function is called, a separate space (called frame) is allocated to this call where all local vars
(incl parms) reside - function statements operate on these

● When a function returns (i.e. completes its execution) - frame is released and local vars disappear

n=4
def factorial(n): Main program (global) variable
if n <= 1:#base case factorial
return 1
On factorial(4) call n=4
else:

On return
return (n * factorial(n-1))
On factorial(3) call n=3

On call
n = 4
print(factorial(n))
On factorial(2) call n=2

On factorial(1) call n=1



Why Recursive Functions ?
● Recursive functions are *not necessary* - anything that can be done with recursion, can
be done without it also
● Recall, some of the older languages did not have recursion

● Many data structures (lists, trees, etc) are naturally recursive - for operations on them
recursion is most natural
● A binary tree: root node + l tree + r tree Recursive vs Iterative
● A list: one item, followed by a list Which of these solutions are better and why?

● Recursive functions are often elegant, and compact!!!

def factorial(n): def factorial(n):


if n <= 1: #base case fact = 1
return 1 for i in range(n, 1, -1):
else: fact = fact * i
Recursive Iterative
return (n * factorial(n-1)) return fact
More recursive problems - Fibonacci
● Fibonacci series: A number in series is the sum of last two numbers in the same series

0, if n = 0,

0 1 1 2 3 5 8 13 …. fib(n) = 1, if n = 1

fib(n-1) + fib(n-2), otherwise (i.e., n >=2)

def fib(n):
if n == 0 or n == 1: #base case
return(n)
else:
return(fib(n-1) + fib(n-2))
More recursive problems - Fibonacci
● Fibonacci series: A number in series is the sum of last two numbers in the same series

fib(n = 4) 3

0 1 1 2 3 5 8 13 …. +

fib(n = 3) 2 fib(n = 2) 1

+ +

fib(n = 2) 1 fib(n = 1) fib(n = 1) 1 fib(n = 0) 0


1
def fib(n): +
if n == 0 or n == 1: #base case
return(n) fib(n = 1) 1 fib(n = 0) 0
else:
return(fib(n-1) + fib(n-2))
Base cases
More recursive problems - Fibonacci
● Fibonacci series: A number in series is the sum of last two numbers in the same series

def fib(n): fib(n = 4) #1


if n == 0 or n == 1:
return(n) +
else:
return(fib(n-1) + fib(n-2)) fib(n = 3) #2 fib(n = 2) #3
+ +

fib(n = 2) #4 fib(n = 1) #5 fib(n = 1) #6 fib(n = 0) #7

What is the correct order of recursion


fib(n = 1) #8 fib(n = 0) #9
call?

1, 2, 4, 8, 9, 5, 2, 6, 7
More recursive problems - Fibonacci
● The current solution is not effective
○ We need three fib(1) calls, two fib(0) and fib(2) calls
fib(n = 4) #1
● How to make it effective?? +
○ Save the computation, to be reused later.
fib(n = 3) #2 fib(n = 2) #3
● Home exercise (ungraded)
+ +
○ You’ll need some data type to store intermediate
results
fib(n = 2) #4 fib(n = 1) #5 fib(n = 1) #6 fib(n = 0) #7

fib(n = 1) #8 fib(n = 0) #9
Example
● Given a pair of recursive functions,
● Write the outcome of the following statements:
● print(f(2) == g(0)) def f(n):
if n == 0:
● print(g(2) + f(2) == 0)
return 1;
● print(g(4) + g(1) == 0)
return f(n-1) + g(n-1);
● print(f(3) + f(0) == 0) def g(n):
● Draw execution (recursion) tree for f(2) and if n == 0 :
compute its value. return 1;
● Draw execution (recursion) tree for g(2) and return g(n-1) - f(n)
compute its value.
More recursive problems: Palindrome
● A piece to text, in which the sequence of characters in both forward and reverse
directions are exactly the same.
● nitin True
P(w =‘nitin’, 0, 4 )
● was it a cat I saw and
True
w[0] == w[4] P(w = ‘nitin’, 1, 3)
True
‘n’ == ‘n’
and

True w[1] == w[3] P(w = ‘nitin’, 2, 2) True


‘i’ == ‘i’

P(w, start, end) = True, if len(w[start: end]) <= 1

w[start] == w[end] and P(w, start+1, end-1) Otherwise


More recursive problems: Palindrome
● A piece to text, in which the sequence of characters in both forward and reverse
directions are exactly the same.
● nitin True
P(w = ‘abba’, 0, 3 )
● was it a cat I saw and
True
w[0] == w[3] P(w = ‘abba’, 1, 2)
True
‘a’ == ‘a’
and

w[1] == w[2] P(w = ‘abba’, 2, 1)True


True ‘b’ == ‘b’

P(w, start, end) = True, if len(w[start: end]) <= 1

w[start] == w[end] and P(w, start+1, end-1) Otherwise


More recursive problems: Palindrome
● A piece to text, in which the sequence of characters in both forward and reverse
directions are exactly the same.
● nitin False
P(w = ‘abca’, 0, 3 )
● was it a cat I saw and
False
w[0] == w[3] P(w = ‘abca’, 1, 2)
True
‘a’ == ‘a’
and

w[1] == w[2] P(w = ‘abca’, 2, 1)True


False ‘b’ == ‘c’
Note: w[2:1] will return an empty
string; hence, length will be 0 and
True will be returned
P(w, start, end) = True, if len(w[start: end]) <= 1

w[start] == w[end] and P(w, start+1, end-1) Otherwise


Searching
● The application of search is everywhere
● One of the most fundamental operation in our day-to-day life
● As trivial as searching for your phone, clothes, etc. when you wake up in the
morning, searching for your friend in the classroom, a seat to sit on, finding
notebooks/pens in your bag, etc.
● To as non-trivial as searching for water on Mars or ALIEN in the universe :)

We need two things in order to perform a search:


● A search space, region, or area
● An item to search
Searching
● Two search algorithms exists
● Linear search (Exhaustive)
● Binary search (Faster)
● Items must be in some order i.e., sorted.
● Items must be directly accessible through indices.

Linear search: Search until we get the item or no Binary search: Divide the space in two parts and
checks only one part everytime, until we get the item or
more space remains
no more space remains
Linear Search

linear-sort(lst, item); lst = [5, 32, 1, 4, 2]


Input: A list of numbers – lst and item = 4
item to search
found = 0
Output: Success with index or failure
for i in range(len(lst)):
if item == lst[i]:
1. For i = 0 to length of lst print ('Item found at index:', i)
2. do
a. If lst[i] == item found = 1
b. then
if not found:
i. return ‘Success with index i’
c. End if print('Item not found')
3. End for
4. return ‘Failure’
Item = 2

Binary Search: Need sorted list


binary-sort(lst, item);
Input: A list of sorted numbers – lst and
item to search start mid end
Output: Success with index or failure

1. end = length of lst


2. start = 0
3. while start <= end lst[2] > item:
4. Do
a. mid = (start + end) / 2 end = 1
b. If lst[mid] == item
c. then
i. return ‘Success with index i’
d. Else if lst[mid] < item
e. then 0 1 2 3 4
i. start = mid+1
f. Else if lst[mid] > item
g. then 1 2 4 5 32
i. end = mid-1
h. End if
5. End while
6. return ‘Failure’
Item = 2

Binary Search: Need sorted list


binary-sort(lst, item);
Input: A list of sorted numbers – lst and
item to search start mid end
Output: Success with index or failure

1. end = length of lst


2. start = 0
3. while start <= end lst[0] < item:
4. Do
a. mid = (start + end) / 2 start = 1
b. If lst[mid] == item
c. then
i. return ‘Success with index i’
d. Else if lst[mid] < item
e. then 0 1 2 3 4
i. start = mid+1
f. Else if lst[mid] > item
g. then 1 2 4 5 32
i. end = mid-1
h. End if
5. End while
6. return ‘Failure’
Item = 2

Binary Search: Need sorted list


binary-sort(lst, item);
Input: A list of sorted numbers – lst and
item to search start mid end
Output: Success with index or failure

1. end = length of lst


2. start = 0
3. while start <= end lst[0] == item:
4. Do
a. mid = (start + end) / 2 found
b. If lst[mid] == item
c. then
i. return ‘Success with index i’
d. Else if lst[mid] < item
e. then 0 1 2 3 4
i. start = mid+1
f. Else if lst[mid] > item
g. then 1 2 4 5 32
i. end = mid-1
h. End if
5. End while
6. return ‘Failure’
Binary Search: Need sorted list
binary-sort(lst, item); lst, item = [1, 2, 4, 5, 32], 5
Input: A list of sorted numbers – lst and
Start, end = 0, len(lst)-1
item to search
found = 0
Output: Success with index or failure
while start <= end:
mid = (start + end) // 2
1. end = length of lst
2. start = 0 if lst[mid] == item:
3. while start <= end print ('Item found at index:', i)
4. Do
a. mid = (start + end) / 2 found = 1
b. If lst[mid] == item Break
c. then
i. return ‘Success with index i’ elif lst[mid] < item:
d. Else if lst[mid] < item start = mid + 1
e. then
i. start = mid+1 elif lst[mid] > item:
f. Else if lst[mid] > item end = mid - 1
g. then
i. end = mid-1
h. End if if not found:
5. End while
6. return ‘Failure’ print('Item not found')
Item = 2

Binary Search: Recursive solution


0 1 2 3 4 0 1 2 3 4
1 2 4 5 32 1 2 4 5 32

Binary_search(lst, 0, 4, item) Binary_search(lst, 0, 1 item)


Compute mid and check. Update start and
end and call recursive function accordingly. Compute mid and check. Update start and
end and call recursive function accordingly.

0 1 2 3 4
1 2 4 5 32

Binary_search(lst, 1, 1, item)
Compute mid and check. Update start and
end and call recursive function accordingly.
Binary Search: Recursive solution
def b_search(A, start, end, item):
if start > end:
return False
else:
mid = (start+end) // 2
if A[mid] == item:
return True
elif A[mid] < item:
start = mid + 1
else:
end = mid - 1
return b_search(A, start, end, item)

A = [1, 2, 3, 5, 32]
print('Item found:', b_search(A, 0, 4, 32)) # True
print('Item found:', b_search(A, 0, 4, 21)) # False
More recursive problems: Tower of Hanoi
Given 3 rods (A, B, C) and n disks.

Problem: Move the entire stack of disks from rod A to C


A : Source
B : Auxiliary
C : Destination

Rules:
1. Only one disk can be moved at a time.
2. A disk can only be moved if it is the uppermost disk in the stack.
3. Larger disk cannot be placed on top of a smaller disk.
ToH Subproblem: Move 1 disk from A to B

A B C A B C

Subproblem
We know how to move one disk!!

ToH Subproblem: Move 2 disks from A to C


A B C A B C

Subproblem

A B C A B C
We know how to move one or two disks!!

ToH Subproblem: Move 3 disks from A to B


A B C A B C

Subproblem

A B C A B C
We know how to move one, two, or three disks!!

Tower of Hanoi: Move 4 disk from A to C


A B C A B C

A B C A B C
Tower of Hanoi
Rules:
1. Only one disk can be moved at a time.
2. A disk can only be moved if it is the uppermost disk in the stack.
3. Larger disk cannot be placed on top of a smaller disk.

“move the disk to Tower Dest”, if n == 1


{
“move the top n-1 disk from Tower Src to Tower Aux”;
ToH(n, Src, Aux, Dest) = “move the remaining 1 disk from Tower Src to Tower Dest”
“move the n-1 disks from Tower Aux to Tower Dest”;
} For n > 1
Care While Using Recursion
● As in while loop, we can have an infinite computation if not done carefully

● When defining a recursive function, must ensure:


● There is a base case - in which no recursive call is made
● Recursive calls are such that eventually the base class will be hit, ending the recursion

● Usually the parameter value of the recursive call will be different from the original call - and
movement towards the direction that the base class will be reached

● Remember, each function call take space in the memory and can lead to a Stack Overflow Error, if
you are not careful.
● To prevent this Python stops recursion at the 1000th recursive call.
● This limit can be modified using the sys.setrecursionlimit(<limit>) function of the sys module.
To be used with caution.

You might also like