# Recursion 2 ## Question What is the output of the following code for N = 3? ```cpp void solve(int N){ if(N == 0) return; solve(N-1); print(N); } ``` **Choices** - [x] 1 2 3 - [ ] 0 1 2 - [ ] 2 1 0 - [ ] 3 2 1 ```cpp void solve(int N) { if (N == 0) return; solve(N - 1); print(N); } ``` N=3 1. So first of all, solve(3) is called, 1. Then solve(3) will first call for solve(2) as n!=0, 1. Similarly, solve(2) calls for solve(1), and then solve(1) calls for solve(0). <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/184/original/Screenshot_2023-10-10_at_5.32.03_PM.png?1696939587" width="150" /> Now n==0 so return. Then solve(1) will print 1, then it will return, and after that solve(2) will print 2, in this way 1, 2, 3 will be printed as an output. --- ### Question What is the output of the following code for N = 3? ```cpp void solve(int N){ if(N == 0) return; print(N); solve(N-1); } ``` **Choices** - [ ] 1 2 3 - [ ] 0 1 2 - [ ] 2 1 0 - [x] 3 2 1 ```cpp void solve(int N){ if(N == 0) return; print(N); solve(N-1); } ``` N=3 1. So first of all, solve(3) is called, 1. Then solve(3) will first **print 3**, then call for solve(2) as n!=0, 1. In this way solve(2) first **print 2**, then call for solve(1), and then solve(1) will **print 1**, then call for solve(0). <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/184/original/Screenshot_2023-10-10_at_5.32.03_PM.png?1696939587" width="150" /> Now n==0 so return. Then solve(1) will return, after that solve(2) will return. In this way 3, 2, 1 will be printed as an output. --- ### Question What is the output of the following code for N = -3? ```cpp void solve(int N){ if(N == 0) return; print(N); solve(N-1); } ``` **Choices** - [ ] -3 -2 -1 - [ ] 3 2 1 - [x] Error; Stack Overflow - [ ] 1 2 3 ```cpp void solve(int N){ if(N == 0) return; print(N); solve(N-1); } ``` `N = -3` In this question we will never reach 0, that's why we are getting stack overflow. At first solve(-3) is called, then it will print -3 call for solve(-4), then it will print -4 call for solve(-5), in this way, it will keep making calls infinitely, as we will not reach zero, hence stack overflow error occurs. --- ## Problem 1 Power Function **Problem Statement** Given two integers **a** and **n**, find **a<sup>n</sup>** using recursion. **Input** ``` a = 2 n = 3 ``` **Output** ``` 8 ``` **Explanation** 2<sup>3</sup> i.e, 2 * 2 * 2 = 8. :::warning Please take some time to think about the recursive approach on your own before reading further..... ::: #### Brute Force Approach The above problem can be redefined as: ``` a ^ n = a * a * a......* a (n times). ``` The problem can be broken into subproblem as: ``` a ^ n = a ^ (n-1) * a ``` So we can say that pow(a,n) is equivalent to ``` pow(a,n) = pow(a,n-1) * a ``` Here, pow(a,n) is the defined as `a^n`. We have seen how the problem is broken into subproblems. Hence, it can be solved using recursion. Below is the algorithm: * Assumption step: * Define a recursive function pow(a,n). * Main Logic: * Define a recursive case: if **n** > 0, then calculate the pow(a,n-1). * Return a * pow(a,n-1). * Base Case: * Base condition: if **n** = 0, then return 1. #### Pseudocode ```cpp function pow(int a, int n) { if (n == 0) return 1; return a * pow(a, n - 1); } ``` #### Complexity We shall calculate Time Complexity at the end. --- ### Power Function Optimized Approach 1 We can also divide pow(a, n) as follows: if **n** is even: ``` pow(a,n) = pow(a,n/2) * pow(a,n/2) ``` if **n** is odd: ``` pow(a,n) = pow(a,n/2) * pow(a,n/2) * a ``` #### Recursion Steps: * Assumption Step: * Define a recursive function pow(a,n). * Main Logic: * if n is odd, then return pow(a,n/2) * pow(a,n/2) * a. * else return pow(a,n/2) * p(a,n/2). * Base Condition: * if **n** is equal to 0, then return 1. #### Pseudocode ```cpp Function pow(int a, int n) { if (n == 0) return 1; if (n % 2 == 0) { return pow(a, n / 2) * pow(a, n / 2); } else { return pow(a, n / 2) * pow(a, n / 2) * a; } } ``` The above function will have more time complexity due to calling the same function twice. We will see it while calculating Time Compleixity. --- ### Time Complexity of Power Function #### Pseudocode ```cpp Function pow(int a, int n) { if (n == 0) return 1; if (n % 2 == 0) { return pow(a, n / 2) * pow(a, n / 2); } else { return pow(a, n / 2) * pow(a, n / 2) * a; } } ``` Let Time taken to calculate pow(a,n) = f(n). ``` T(n) = 2 * T(n/2) + 1 ``` Substituting the value of T(n/2) = 2 * T(n/4) + 1 ``` T(n) = 2 * [2 * T(n/4) + 1] + 1 = 4 * T(n/4) + 3 = 2^2 * T(n/2^2) + (2^2 - 1) ``` Substituting the value of T(n/4) = 2 * T(n/8) + 1 ``` T(n) = 4 * [2 * T(n/8) + 1] + 3 = 8 * T(n/8) + 7 = 2^3 * T(n/2^3) + (2^3 - 1) ``` Substituting the value of T(n/8) = 2 * T(n/16) + 1 ``` T(n) = 8 * [ 2 * T(n/16) + 1] + 7 = 16 * T(n/16) + 15 = 2^4 * T(n/2^4) + (2^4 - 1) ``` After, say, **k** iterations, we shall reach the base step. The equation will be: ``` T(n) = 2^k * T(n/2^k) + (2^k - 1) ``` The base case shall take contant time: ``` T(0) = O(1) or T(1) will also be constant ``` ``` n/(2 ^ k) = 1 n = 2^k k = log2(n) ``` Hence we can say that ``` T(n) = n * T(1) + (n - 1) = O(n) ``` Let's see time complexity of the optimised pow function. --- ### Power Function Optimized Approach - Fast Power In above approach, we are calling function **pow(a, n/2)** twice. Rather, we can just call it once and use the result twice. Below is the algorithm: * Assumption Step: * Define a recursive function **pow(a,n)**. * Main Logic: * Calculate **pow(a,n/2)** and store it in a variable **p**. * if n is odd, then return **p * p * a**. * else return **p * p**. * Base Condition: * if **n = 0**, then return **1**. #### Pseudocode ```cpp Function pow(int a, int n) { if (n == 0) return 1; int p = pow(a, n / 2); if (n % 2 == 0) { return p * p; } else { return p * p * a; } } ``` > Note: The above function is known as Fast Power or Fast Exponentiation. --- ### Time Complexity of Fast Power #### Pseudocode ```cpp Function pow(int a, int n) { if (n == 0) return 1; long p = pow(a, n / 2); if (n % 2 == 0) { return p * p; } else { return p * p * a; } } ``` Let time taken to calculate pow(a,n) = f(n). Recurrence Relation is: ``` T(n) = T(n/2) + 1 ``` Substituting the value of T(n/2) = T(n/4) + 1 ``` T(n) = [T(n/4) + 1] + 1 = T(n/4) + 2 ``` Substituting the value of T(n/4) = T(n/8) + 1 ``` T(n) = [T(n/8) + 1] + 2 = T(n/8) + 3 ``` Substituting the value of T(n/8) = T(n/16) + 1 ``` T(n) = [T(n/16) + 1] + 3 = T(n/16) + 4 ``` After say **k** iterations, we shall reach to the base step. The equation will be: ``` T(n) = T(n/2^k) + k ``` Base case shall take constant time: ``` T(0) = O(1) or T(1) will also be constant ``` ``` n/(2 ^ k) = 1 n = 2^k k = log2(n) ``` Hence we can say that ``` T(n) = T(1) + log2(n) = O(log2(n)) ``` --- ### Question How many recursive call in the FAST pow(2,5)? Choose the correct answer **Choices** - [ ] 0 - [ ] 2 - [x] 4 - [ ] 5 This is ~ log N calls. Therefore, time complexity of sum of digits = O(log N) * 1 = O(log N) --- ### Space Complexity of pow(a,n) There are total **log2(N)** recursive calls as shown below: ``` pow(a,0) pow(a,1) pow(a,2) pow(a,4) --------------- --------------- --------------- sumofdigits(a,N/2) sumofdigits(a,N) ``` Hence, the total O(log2(N)) space required to execute all the recursive calls. --- ### Problem 2 Tower of Hanoi There are n disks placed on tower A of different sizes. **Goal** Move all disks from tower A to C using tower B if needed. **Constraint** - Only 1 disk can be moved at a time. - Larger disk can not be placed on a small disk at any step. Print the movement of disks from A to C in minimum steps. **Example 1** **Input:** N = 1 <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/185/original/Screenshot_2023-10-10_at_5.32.25_PM.png?1696939687" width="500" /> **Explanation:** <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/187/original/Screenshot_2023-10-10_at_5.32.32_PM.png?1696939777" width="500" /> **Output:** 1: A -> C **Example 2** **Input:** N = 2 <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/188/original/Screenshot_2023-10-10_at_5.32.40_PM.png?1696939808" width="500" /> **Explanation:** 1: A -> B <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/190/original/Screenshot_2023-10-10_at_5.32.50_PM.png?1696939875" width="500" /> 2: A -> C <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/192/original/Screenshot_2023-10-10_at_5.32.57_PM.png?1696939912" width="500" /> 1: B -> C <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/193/original/Screenshot_2023-10-10_at_5.33.04_PM.png?1696939944" width="500" /> **Output:** 1: A -> B 2: A -> C 1: B -> C **Example 3** **Input:** N = 3 <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/198/original/Screenshot_2023-10-10_at_5.34.12_PM.png?1696940309" width="500" /> **Explanation:** 1: A -> C 2: A -> B 1: C -> B <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/199/original/Screenshot_2023-10-10_at_5.33.19_PM.png?1696940471" width="500" /> 3: A -> C <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/202/original/Screenshot_2023-10-10_at_5.33.27_PM.png?1696940631" width="500" /> 1: B -> A 2: B -> C 1: A -> C <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/203/original/Screenshot_2023-10-10_at_5.33.34_PM.png?1696940669" width="500" /> **Output:** 1: A -> C 2: A -> B 1: C -> B 3: A -> C 1: B -> A 2: B -> C 1: A -> C #### n disks <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/204/original/Screenshot_2023-10-10_at_5.33.41_PM.png?1696940720" width="500" /> **Step 1:** Move (n-1) disks from A to B <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/205/original/Screenshot_2023-10-10_at_5.33.48_PM.png?1696940764" width="500" /> **Step 2:** Move n disk to C <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/206/original/Screenshot_2023-10-10_at_5.33.55_PM.png?1696940798" width="500" /> **Step 3:** Move (n-1) disks from B to C <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/207/original/Screenshot_2023-10-10_at_5.34.03_PM.png?1696940834" width="500" /> #### Pseudocode ```cpp src temp dest void TOH(int n, A, B, C){ 1. if(n == 0) return; 2. TOH(n - 1, A, C, B); // move n-1 disks A->B 3. print(n : A -> C); // moving n disk A->C 4. TOH(n - 1, B, A, C); // move n-1 disks B->C } ``` #### Dry Run **n = 3** <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/198/original/Screenshot_2023-10-10_at_5.34.12_PM.png?1696940309" width="500" /> <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/208/original/Screenshot_2023-10-10_at_5.35.05_PM.png?1696941053" width="450" /> **Output:** 1: A -> C 2: A -> B 1: C -> B 3: A -> C 1: B -> A 2: B -> C 1: A -> C #### Time Complexity It can be observed in terms of number of steps taken for N <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/160/original/Screenshot_2023-10-10_at_2.30.48_PM.png?1696928475" width="500" /> #### Space Complexity <img src="https://d2beiqkhq929f0.cloudfront.net/public_assets/assets/000/053/161/original/Screenshot_2023-10-10_at_2.33.36_PM.png?1696928627" width="500" />