0% found this document useful (0 votes)
2 views

lec15-recursion

Uploaded by

Mamat Rahmat
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

lec15-recursion

Uploaded by

Mamat Rahmat
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 33

CSCI1540

Fundamental Computing
with C++
Recursion

Fall, 2024
What is Recursion?

“In order to understand recursion,


you must first understand recursion”

2
Basic Elements of Recursion
• A recursive function is a function that contains a call
to itself either directly or indirectly
• It can be considered an advanced form of control flow,
an alternative to iteration/repetition
int foo(…) {

… foo(…) …; // Calls itself

}

• Recursion usually leads to more elegant and


simpler solutions, but incurs larger memory and
time overhead
3
Basic Elements of Recursion
• Writing recursive programs requires “recursive
thinking style” and “recursive problem-solving
skills”, which can only be acquired through practice
and experience

• An important recursive problem-solving skill is


divide-and-conquer
• Divide the problem into smaller pieces
• Tackle each sub-task either directly or by recursion
• Combine the solutions of the parts to form the solution
of the whole

4
Counting Down (Iterative):
Example
• Suppose we need to write a function that counts
down from 𝑁 (> 0) to 1, printing 𝑁, … , 2,1 in turn
An iterative 1 #include <iostream>
solution: 2 using namespace std;
3
4 void countDown(int n) {
5 for (int i = n; i > 0; i--)
6 cout << i << endl;
7 }
8 5
9 int main() { 4
10 countDown(5); 3
11 return 0; 2
12 } 1
5
Counting Down (Recursion):
Example
• A recursive solution:
• If 𝑁 < 1, then do nothing and done!
• If 𝑁 ≥ 1, then the solution consists of two sub-tasks:
i. Print 𝑁; and
ii. Count down from 𝑁 − 1 to 1

1 void countDown(int n) { Test


2
3 if (n >= 1) {
4 cout << n << endl;
5 countdown( n - 1 );
6 } // else nothing
7 Recursive call
8 }
Base case (do nothing) 6
Basic Elements of Recursion
• Any recursive function will include the following
three basic elements:

• A test to stop or continue the recursion

• A base case that terminates the recursion

• A recursive call that continues the recursion

7
Tracing Recursion
void countDown(int n) {
if (n >= 1) {
int main() { cout << n << endl;
countDown(5); countdown( n - 1 );
return 0; } // else nothing
} }
cout << 5 << endl;
countdown( 4 );
cout << 4 << endl;
countdown( 3 );
cout << 3 << endl;
countdown( 2 );
5 cout << 2 << endl;
4 countdown( 1 );
3 cout << 1 << endl;
2 countdown( 0 );
1 (Do nothing!) 8
Iteration vs Recursion
Iteration Recursion
(while, do-while, for)
• Maintains and modifies loop • Keeps producing simpler
variables in a termination versions of the original
condition problem
• Until the condition is met • Until some base cases are
reached
void countDown(int n) { void countDown(int n) {
for (int i = n; i > 0; i--) if (n >= 1) {
cout << i << endl; cout << n << endl;
} countdown(n - 1);
} // else nothing
}
9
Summation 1 + 2 + ⋯ + 𝑛:
Example
𝑛
• To compute:
෍𝑖 = 1 + 2 + 3 + ⋯+ 𝑛
𝑖=1
= 1 + 2 + ⋯+ 𝑛 − 1 +𝑛
𝑛−1

(Assume 𝑛 > 0) =𝑛+ ෍𝑖


𝑖=1

1 int summation(int n) { Test


2 if (n == 1)
3 return 1; Base case
4 else
5 return n + summation( n - 1 );
6 }
Recursive call 10
Summation 1 + 2 + ⋯ + 𝑛:
Example
1 int summation(int n) {
2 if (n == 1)
3 return 1;
4 else
5 return n + summation( n - 1 );
6 }
7
8 int main() {
9 int n;
10 cout << "Enter n: "; Enter n: 5↵
11 cin >> n; Sum = 15
12 cout << "Sum = " << summation(n) << endl;
13 return 0; Enter n: 14↵
14 } Sum = 105

11
Summation 1 + 2 + ⋯ + 𝑛:
Example
summation(5)
Recursive

→ 5 + summation(4)
calls

→ 5 + (4 + summation(3))
→ 5 + (4 + (3 + summation(2)))
→ 5 + (4 + (3 + (2 + summation(1))))
→ 5 + (4 + (3 + (2 + 1)))
Combining
solution

→ 5 + (4 + (3 + 3))
→ 5 + (4 + 6)
int summation(int n) {
→ 5 + 10 if (n == 1)
→ 15 return 1;
else
return n + summation(n - 1);
} 12
String Reversal: Example
• rev("abcde") → “edcba”
• rev("CSCI1357") → “7531ICSC”
• Reversing an empty string gets an empty string
• Reversing a non-empty string gets…
1 string rev(string s) { rev("abcde")
2 if (s == "") → rev("bcde") + 'a'
3 return "";
4 else
5 return rev(s.substr(1)) + s.at(0);
6 }

The substring of s substr(…) is a member


from index 1 till end function of the string class 13
Exercise
• Write a recursive function (no loop inside)
int count(string s, char ch);
that counts the number of times that character ch
appears in string s
• E.g.:
count("Computer Science", 'e') → 3
count("Computer Science", 'a') → 0
if (…) // Base case
• You need > 1 recursive case

else if (…) // Recur. case 1

else // Recur. case 2
… 14
Tower of Hanoi: Example
• The Tower of Hanoi is a puzzle consisting of 3 pegs
and 𝑁 disks of different sizes

𝑁 disks 𝑁=4
Peg 1 Peg 2 Peg 3

15
Tower of Hanoi
• The goal is to move the 𝑁 disks from peg 1 to peg 3
satisfying the following constraints:
• Only one disk can be moved at a time
• A larger disk cannot be on top of a smaller disk

(Move
disks)

Peg 1 Peg 2 Peg 3 Peg 1 Peg 2 Peg 3


Start Goal
16
Tower of Hanoi: Recursion
• Solution for 𝑁 = 1 is trivial: (Base case)

• Move the (only) disk to the required destination

Peg 1 Peg 2 Peg 3

17
Tower of Hanoi: Recursion
• Solution for 𝑁 > 1: (Recursive case)

• “Step” 1: Move the top 𝑁 − 1 disks from peg 1 to peg 2

• Step 2: Move the remaining disk from peg 1 to peg 3

• “Step” 3: Move the 𝑁 − 1 disks from peg 2 to peg 3

“Step” 1 “Step” 3

Step 2

Peg 1 Peg 2 Peg 3 18


Tower of Hanoi Solution
Recursion
“Step” 1
Peg 1 Peg 2 Peg 3 Peg 1 Peg 2 Peg 3
Move 𝑁 − 1 disks
from Peg 1 to Peg 2

Move one

Step 2
disk from Peg
1 to Peg 3
Recursion
Move 𝑁 − 1 disks
from Peg 2 to Peg 3

“Step” 3
Peg 1 Peg 2 Peg 3 Peg 1 Peg 2 Peg 3
19
Tower of Hanoi Solution Original peg
Destination peg
1 void hanoi(int n, int from, int to, int buffer) {
2 if ( n == 1 ) { “Middle” peg
3 cout << from << " --> " << to << endl;
4 } else {
5 hanoi(n - 1, from, buffer, to); // "Step" 1
6 cout << from << " --> " << to << endl; // Step 2
7 hanoi(n - 1, buffer, to, from); // "Step" 3
8 }
9 }
10
11 int main() {
12 int n;
13 cout << "Enter n: ";
14 cin >> n;
15 hanoi(n, 1, 3, 2); // From peg 1 to 3 via 2
16 return 0;
17 } 20
Enter n: 5↵
1 --> 3
1 --> 2
3 --> 2
Tower of Hanoi Solution 1 -->
2 -->
3
1
2 --> 3
1 --> 3
1 --> 2
Enter n: 1↵ Enter n: 4↵ 3 --> 2
1 --> 3 1 --> 2 3 --> 1
2 --> 1
1 --> 3 3 --> 2
Enter n: 2↵ 2 --> 3 1 --> 3
1 --> 2 1 --> 2 1 --> 2
1 --> 3 3 --> 1 3 --> 2
2 --> 3 1 --> 3
3 --> 2 2 --> 1
1 --> 2 2 --> 3
Enter n: 3↵ 1 --> 3
1 --> 3 2 --> 1
1 --> 3
2 --> 3 3 --> 2
1 --> 2
2 --> 1 3 --> 1
3 --> 2 2 --> 1
3 --> 1
1 --> 3 2 --> 3
2 --> 3 1 --> 3
2 --> 1
1 --> 2 1 --> 2
2 --> 3 3 --> 2
1 --> 3
1 --> 3 1 --> 3
2 --> 3 2 --> 1
2 --> 3
1 --> 3 21
Tower of Hanoi: 𝑁 = 3
void hanoi(int n, int from, int to, int buffer) { Enter n: 3↵
if ( n == 1 ) { 1 --> 3
cout << from << " --> " << to << endl; 1 --> 2
} else { 3 --> 2
hanoi(n - 1, from, buffer, to); // "Step" 1 1 --> 3
cout << from << " --> " << to << endl; // Step 2 2 --> 1
hanoi(n - 1, buffer, to, from); // "Step" 3 2 --> 3
} 1 --> 3
}
hanoi(3, 1, 3, 2); (3 disks, Peg 1 to 3 via 2)

hanoi(2, 1, 2, 3); hanoi(1, 1, 3, 2); cout << "1 --> 3\n";


cout << "1 --> 3\n"; cout << "1 --> 2\n";
hanoi(2, 2, 3, 1); hanoi(1, 3, 2, 1); cout << "3 --> 2\n";

hanoi(1, 2, 1, 3); cout << "2 --> 1\n";


cout << "2 --> 3\n";
hanoi(1, 1, 3, 2); cout << "1 --> 3\n";
22
Tower of Hanoi: 𝑁 = 3

Enter n: 3↵
Peg 1 Peg 2 Peg 3 1 --> 3
1 --> 2
3 --> 2
1 --> 3
1→3 2 --> 1
Peg 1 Peg 2 Peg 3 2 --> 3
1 --> 3

1→2
Peg 1 Peg 2 Peg 3

3→2
Peg 1 Peg 2 Peg 3 23
Tower of Hanoi: 𝑁 = 3

1→3 Enter n: 3↵
Peg 1 Peg 2 Peg 3 1 --> 3
1 --> 2
3 --> 2
1 --> 3
2→1 2 --> 1
Peg 1 Peg 2 Peg 3 2 --> 3
1 --> 3

2→3
Peg 1 Peg 2 Peg 3

1→3 Done!
Peg 1 Peg 2 Peg 3 24
Tower of Hanoi: 𝑁 = 4
hanoi(4, 1, 3, 2); (4 disks, Peg 1 to 3 via 2)
Enter n: 4↵
1 --> 2 hanoi(1, 1, 2, 3);
1 --> 3 hanoi(2, 1, 3, 2);
2 --> 3 hanoi(1, 2, 3, 1);
1 --> 2 hanoi(3, 1, 2, 3);
3 --> 1 hanoi(1, 3, 1, 2);
3 --> 2 hanoi(2, 3, 2, 1);
1 --> 2 hanoi(1, 1, 2, 3);
1 --> 3
2 --> 3 hanoi(1, 2, 3, 1);
2 --> 1 hanoi(2, 2, 1, 3);
3 --> 1 hanoi(1, 3, 1, 2);
2 --> 3 hanoi(3, 2, 3, 1);
1 --> 2 hanoi(1, 1, 2, 3);
1 --> 3 hanoi(2, 1, 3, 2);
2 --> 3 hanoi(1, 2, 3, 1);
25
Pitfall of Recursion
• Recursive function calls incur both time and
memory overheads since a runtime stack must be
maintained for the function calls

• Space to store the growing runtime stack

• Time to add and remove information from the runtime


stack

26
Tower of Hanoi
• It has been proved that a Tower of Hanoi of 𝑁 disks
can be solved in 2𝑁 − 1 moves

• 3 disks → 7 moves
• 4 disks → 15 moves

• 10 disks → 1,023 moves

• 64 disks → 18,446,744,073,709,551,615 moves

27
Fibonacci Series: Example
• The Fibonacci Series

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …

• Begins with 0 and 1

• Each subsequent Fibonacci number is the sum of the


previous two Fibonacci numbers in the series

28
Fibonacci Series: Recursion
0 𝑛=0
𝑓𝑖𝑏(𝑛) = ቐ 1 𝑛=1
𝑓𝑖𝑏 𝑛 − 2 + 𝑓𝑖𝑏(𝑛 − 1) 𝑛≥2

• 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, …


1 long long fib( int n ) {
2 if (n == 0)
3 return 0;
4 else if (n == 1)
5 return 1;
6 else
7 return fib(n - 2) + fib(n - 1);
8 }
29
Excessive Repetition
• Carelessly designed recursive functions can lead to
very inefficient and unacceptable solutions
fib(5)
Recursive Fibonacci ends up
repeating the same
fib(3) + fib(4)
computation numerous times!

fib(1) + fib(2) fib(2) + fib(3)

fib(0) + fib(1) fib(0) + fib(1) fib(1) + fib(2)

fib(0) + fib(1)
30
Non-Recursive Fibonacci (Iteration)
1 long long fib( int n ) {
2 long long fibN, fibN1, fibN2;
3
4 if (n == 0) {
5 return 0;
6 } else if (n == 1) {
7 return 1;
8 } else {
9 fibN1 = 0;
10 fibN2 = 1;
11 for (int cnt = 2; cnt <= n; cnt++) {
12 fibN = fibN1 + fibN2; // Get the next fib num
13 fibN1 = fibN2;
14 fibN2 = fibN;
15 }
16 return fibN;
17 }
18 } 31
When (Not) to Use Recursion?
• In general, use recursion if:

• A recursive solution is natural and easy to understand

• A recursive solution does not result in excessive


duplicate computation

• The equivalent iterative solution is too complex

No really clear cut guidelines!!!

32
Summary
• Recursive functions have the following elements
• Different cases are governed by selection (if-else)

• One or more base cases have simple and non-recursive


solution

• Recursion is used to reduce the problem to cases that


are simpler and closer to the base case

• Recursion continues until a base case is reached and


answers of the parts are returned and combined to form
the answer of the whole

Next: Dynamic Memory Management 33

You might also like