Recursion
Introduction to Recursion
- Recursion is a fundamental programming technique offering elegant solutions to certain problems.
- Key aspects include:
- Thinking recursively.
- Programming recursively.
- Correct usage of recursion.
- Illustrative examples.
Recursive Definitions
- Recursion: Solving a problem by reducing it to smaller instances of itself.
- Recursive Definition: Expressing a problem in terms of its smaller version(s).
- Base Case: Essential condition(s) that directly provide a solution without further recursion.
Infinite Recursion
- Base Case Requirement: All recursive definitions must include a non-recursive base case to ensure termination.
- Infinite Recursion: Absence of a base case leads to a non-terminating recursive path.
- Analogy: Similar to an infinite loop, but inherent in the definition itself.
Recursive Factorial
- Definition: For a positive integer N, N! is the product of all integers from 1 to N.
- Recursive Expression:
- Base Case: The recursion eventually reaches the base case of 1!.
Recursive Definitions (continued)
- 0! = 1
- n! = n \times (n-1)! if n>0
- Example Calculation:
- 3! = 3 \times 2!
- 2! = 2 \times 1!
- 1! = 1 \times 0!
- 0! = 1
- 1! = 1
- 2! = 2 \times 1 = 2 \times 1 = 2
- 3! = 3 \times 2! = 3 \times 2 = 6
Computing Factorial (Illustrative Animation)
- Base case: factorial(0) = 1
- Recursive case: factorial(n) = n * factorial(n-1)
- factorial(4) is computed through a series of recursive calls.
Step-by-Step Factorial Computation
- factorial(4) = 4 * factorial(3)
- factorial(4) = 4 * 3 * factorial(2)
- factorial(4) = 4 * 3 * (2 * factorial(1))
- factorial(4) = 4 * 3 * (2 * (1 * factorial(0)))
- factorial(4) = 4 * 3 * (2 * (1 * 1))
- factorial(4) = 4 * 3 * (2 * 1)
- factorial(4) = 4 * 3 * 2
- factorial(4) = 4 * 6
- factorial(4) = 24
Recursive Factorial Method in Java
public static int fact(int num) {
if (num == 0)
return 1;
else
return num * fact(num - 1);
}
Quick Check: Recursive Definition of 5 * n
- Base case: 5 * 1 = 5
- Recursive case: 5 * n = 5 + (5 * (n-1))
Recursive Programming
- Recursive Method: A method that calls itself.
- Structure: Must handle both the base case and the recursive case.
- Execution Environment: Each call creates a new environment with new parameters and local variables.
- Control Flow: Upon completion, control returns to the calling method (which may be an earlier invocation of itself).
Recursive Definitions (continued)
- Recursive Algorithm: Solves a problem by reducing it to smaller instances.
- Base Case: Essential to stop the recursion, providing a direct solution.
- Recursive Method: Implements the recursive algorithm by calling itself.
- General Solution: Breaks down the problem into smaller versions.
- General Case: Calls a smaller version of itself and must eventually lead to the base case.
Designing Recursive Methods
- Understand problem requirements.
- Determine limiting conditions.
- Identify base cases.
Sum of 1 to N
- Problem: Compute the sum of all numbers between 1 and a positive integer N.
Recursive Implementation for Summation
public int sum(int num) {
int result;
if (num == 1)
result = 1;
else
result = num + sum(num-1);
return result;
}
Recursive Programming Considerations
- Efficiency: While recursion can solve problems, it's not always the best approach.
- Understandability: Iterative solutions are often easier to understand for simple problems like summation.
- Elegance: Recursion can provide cleaner solutions for some problems.
- Decision: Carefully evaluate whether recursion is the correct technique for a given problem.
Indirect Recursion
- Direct Recursion: A method calls itself directly.
- Indirect Recursion: A method calls another method, which eventually calls the original method again (e.g., m1 calls m2, which calls m3, which calls m1).
- Complexity: Requires the same care as direct recursion but can be more difficult to trace and debug.
Largest Value in Array (Recursive Approach)
- Base Case: If the list size is 1, the largest element is the only element.
- Recursive Step: To find the largest element in list[a]…list[b]:
- Find the largest element in list[a + 1]…list[b] and call it max.
- Compare list[a] and max.
- If list[a] >= max, the largest element is list[a].
- Otherwise, the largest element is max.
public static int largest(int[] list, int lowerIndex, int upperIndex) {
int max;
if (lowerIndex == upperIndex)
return list[lowerIndex];
else {
max = largest(list, lowerIndex + 1, upperIndex);
if (list[lowerIndex] >= max)
return list[lowerIndex];
else
return max;
}
}
Execution of Largest
- Example: largest(list, 0, 3) with list = {5, 10, 12, 8}
Recursive Fibonacci
- Definition: rFibNum(a, b, n)
- a if n = 1
- b if n = 2
- rFibNum(a, b, n-1) + rFibNum(a, b, n-2) if n>2.
public static int rFibNum(int a, int b, int n) {
if (n == 1)
return a;
else if (n == 2)
return b;
else
return rFibNum(a, b, n -1) +
rFibNum(a, b, n - 2);
}
Fibonacci Numbers
- Fibonacci Series: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89,…
- Indices: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
- Recursive Definition:
- fib(0) = 0
- fib(1) = 1
- fib(index) = fib(index -1) + fib(index -2) for index >= 2
- Example: fib(3) = fib(2) + fib(1) = (fib(1) + fib(0)) + fib(1) = (1 + 0) + fib(1) = 1 + fib(1) = 1 + 1 = 2
Fibonacci Numbers (cont.)
- Illustrates the call order and return values for fib(4), showcasing multiple calls to fib(1) and fib(0).
Recursive Palindrome in Java
public class RecursivePalindrome {
public static boolean isPalindrome(String s) {
return isPalindrome(s, 0, s.length() - 1);
}
private static boolean isPalindrome(String s, int low, int high) {
if (high <= low) // Base case
return true;
else if (s.charAt(low) != s.charAt(high)) // Base case
return false;
else
return isPalindrome(s, low + 1, high - 1);
}
public static void main(String[] args) {
System.out.println("Is moon a palindrome? " + isPalindrome("moon"));
System.out.println("Is noon a palindrome? " + isPalindrome("noon"));
System.out.println("Is a a palindrome? " + isPalindrome("a"));
System.out.println("Is aba a palindrome? " + isPalindrome("aba"));
System.out.println("Is ab a palindrome? " + isPalindrome("ab"));
}
}
Characteristics of Recursion
- Base Cases: One or more base cases are used to stop recursion.
- Reduction: Every recursive call reduces the original problem, bringing it closer to a base case.
- Subproblems: Recursion solves a problem by breaking it into subproblems.
- Resemblance: If a subproblem resembles the original problem, the same approach can be applied recursively.
- Smaller Size: Subproblems are almost the same as the original problem but with a smaller size.