1unit 7
Unit 7: RecursionUnit 7: Recursion
Recursion is a fundamental programming technique that can provide an elegant solution to a certain kinds of problems
In recursion we solve a problem by reducing it to a similar problem of “smaller size”
basic programming
concepts
object oriented programming
topics in computer science
syllabus
2unit 7
Factorial Example: implementation 1Factorial Example: implementation 1
Problem: find the factorial of an integer number, defined for a positive number as follows:
n!=1*2*3*…*n
A simple implementation would use a loop that goes over the expression and keeps track of the partial multiplication
3unit 7
Factorial Example: code 1Factorial Example: code 1
// Computes the factorial of a number
public static long factorial(long n) {
long multiplication = 1;
for (int i=1; i<n; i++) {
multiplication *= i;
}
return multiplication;
}
4unit 7
Factorial Example:implementation 2Factorial Example:implementation 2
We observe that for every integer n, n>1 :
n! = 1*2*...n = (1*2*...n-1)*n = (n-1)! * n
So we have:
1! = 1
n! = (n-1)!*n, for n>1
We can use these observations to derive a new implementation for computing n!
5unit 7
Computing Computing nn!!
If n is 1, n!=1 If n>1, use n!=(n-1)!*n
But do we know how to compute (n-1)! ?
Since the reduction holds for all n>1, we can reduce the problem from n! to (n-1)! to (n-2)! etc, until we reach the problem of computing 1! which is easy
6unit 7
Factorial recursive computationFactorial recursive computation
5!
5 * 4!
4 * 3!
3 * 2!
2 * 1!
1
2
6
24
120
7unit 7
Factorial Example: code 2 Factorial Example: code 2
// Computes the factorial of a number.
public static long factorial(long n) {
if (n==1) {
return 1;
}
return n*factorial(n-1);
}
8unit 7
Factorial Example: proof of correctnessFactorial Example: proof of correctness
We can prove that our implementation of ‘factorial’ is correct using mathematical induction
We have to show the following• factorial gives the correct result for n=1
• If ‘factorial’ returns the correct answer for an arbitrary number n such that n>1, then ‘factorial’ returns the correct answer for n+1
Now the proof is immediate
9unit 7
Factorial Example: shorter code 2Factorial Example: shorter code 2
// Computes the factorial of a number.
public static long factorial(long n) {
return (n==1) ? 1 : n*factorial(n-1);
}
10unit 7
Recursive ProgrammingRecursive Programming
A method in Java can invoke itself; if set up that way, it is called a recursive method
A recursive method has:• a base case which solves the task without a recursive
call• a recursive step which solves the task by reducing it
and using a recursive call
If there is no base case the sequence of reductions will not terminate; we call this an infinite recursion
11unit 7
isNumeric() ExampleisNumeric() Example
Problem: check if a given string consists only of digits
We want to write a method isNumeric() that for a given string would return true only if all the characters in the string are in ‘0’, ‘1’, ..., ‘9’
A simple implementation would go over the characters of the string in a loop and return false if a non-digit character is found
12unit 7
isNumber(): implementation 1isNumber(): implementation 1
// Checks if a given string consists only
// Of digit symbols
public static boolean isNumeric(String s) {
for (int i=0; i<s.length(); i++) {
char c = s.charAt(i);
if (c<‘0’ || c>’9’) {
return false;
}
}
return true;
}
13unit 7
isNumeric(): recursive formulationisNumeric(): recursive formulation
Base case: an empty string doesn’t contain non-digit characters, therefore return true for the empty string
If the first character of s is non-digit then return false
Recursive step: if the first character of s is a digit symbol, then s is numeric if and only if the rest of s (without the first character) is numeric
14unit 7
isNumber(): implementation 2isNumber(): implementation 2
// Checks if a given string consists only
// of digit symbols.
public static boolean isNumeric(String s) {
if (s.length()==0) {
return true;
}
char c = s.charAt(0);
return (c>=‘0’ && c<=‘9’ &&
isNumeric(s.substring(1));
}
15unit 7
Fibonacci Series ExampleFibonacci Series Example
Fibonacci series is a series of integers f0, f1, f2, ... that is defined as follows:
f0 = 1
f1 = 1
fn = fn-1+fn-2 for n>1
16unit 7
Fibonacci Example: implementationFibonacci Example: implementation
// Returns the n number in Fibonacci series
public static int fibonacci(int n) {
if (n<=0) {
return 1;
}
return fibonacci(n-1)+fibonacci(n-2);
}
issue of efficienty
17unit 7
Recursive ProgrammingRecursive Programming
Note that just because we can use recursion to solve a problem, doesn't mean we should (there is a cost associated with multiple method calls)
In fact, we usually would not use recursion to solve the previous problems (why?)
However, for some problems, recursion provides an elegant solution, often cleaner than an iterative version
We will see next a few examples where a recursive solution is very appropriate
18unit 7
GCD ExampleGCD Example
The Greatest Common Divisor of two integers a,b is defined as the largest integer that divides both a and b; we denote the greatest common divisor of a and b by gcd(a,b)
Consider the problem of finding the gcd of twointegers, where w.l.o.g. a>b: Base case: if b divides a, then gcd(a,b)=b (because b
also divides itself and there is no greater integer that divides b)
Recursive step: if b doesn’t divide a...
19unit 7
GCD Example: GCD Example: Recursive StepRecursive Step
• Let m denote the remainder of the division of a by b (m=a%b), then:
a = k*b+m, m<b
• gcd(a,b)=gcd(b,m) because every integer that divides both a and b must also
divide m every integer that divides both b and m must also
divide a Recursive step: gcd(a,b)=gcd(b,a%b) Here the recursive solution is very appropriate
20unit 7
Example: the Catalan numberExample: the Catalan number
The nth Catalan number - Cn - counts the number of valid strings of length 2n which:• consist of n '(' symbols and n ')' symbols
• in every prefix the number of '(' symbols are at least as big as the number of ')' symbols
examples: "(()())" is valid, "())(()" is not Base case: C0=1 - there is only a single string of
size 0, the empty string "" Recursive step: for n>0, Cn=k=1..n(Ck-1*Cn-k)
21unit 7
Catalan ExampleCatalan Example
// Return the n'th Catalan number.
public static int catalan(int n) {
if (n==0) {
return 1;
}
int sum = 0;
for (int k=1; k<=n; k++) {
sum += catalan(k-1)*catalan(n-k);
}
return sum;
}
22unit 7
hasPartialSum() ExamplehasPartialSum() Example
Problem: given a series of integers a1, a2, ..., an and a number s, we want to determine whether there is a partial series whose elements sum up to s
Base case: if the series is empty, the answer is true iff s is zero
Recursive step: otherwise• if the last element is not included in the partial series,
reduce the problem to finding a partial sum s in the rest of the series
• if the last element is included, reduce the problem to finding a partial sum s- aN in the rest
23unit 7
hasPartialSum(): codehasPartialSum(): code
// Check if a given series of integers has a partial
// series whose sum equals a given integer.
// length indicates the taken length of the series
public static boolean hasPartialSum(
int[] series, int sum, int length) {
if (length==0) {
return sum==0;
}
return hasPartialSum(series, sum, length-1) ||
hasPartialSum(series, sum-series[length-1],
length-1);
}
40unit 7
Towers of Hanoi: schematic codeTowers of Hanoi: schematic code
// Solves the Towers of Hanoi problem for a
// given number of disks.
public static void solve(
Pile from, Pile to, Pile using, int n) {
if (n==1) {
to.addDisk(from.removeDisk());
return;
}
solve(from, using, to, n-1);
solve(from, to, using, 1);
solve(using, to, from, n-1);
}
}
41unit 7
File System ExampleFile System Example
Suppose we want to list all the files under a given folder:
• Let L be the list of all files and folders directly under f
• For each element f1 in L : if f1 is a folder, list folders under f1 (recursive step) otherwise, print the name of f1 (base case)
42unit 7
File System Example File System Example
import java.io.File;
// List all files under a given folder public static void listFiles(File folder) { String[] files = folder.list(); for (int i=0; i<files.length; i++) { File file = new File(folder, files[i]); if (file.isDirectory()) { listFiles(file); } else { System.out.println(file); } } }
43unit 7
Indirect RecursionIndirect Recursion
A method invoking itself is considered to be direct recursion
A method could invoke another method, which invokes another, etc., until eventually the original method is invoked again
For example, method m1 could invoke m2, which invokes m3, which in turn invokes m1 again
This is called indirect recursion, and requires all the same care as direct recursion
It is often more difficult to trace and debug
44unit 7
How does recursion work?How does recursion work?
Let’s examine again the implementation of factorial
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
This solution may appear magical, since we never explicitly say how to compute factorial
45unit 7
How does recursion work?How does recursion work?
In the non-recursive solution the code describes the whole process of the computation:
public static long factorial(int n) {
long multiplication = 1;
for (int i=1; i<n; i++) {
multiplication *= i;
}
return multiplication;
}
This implementation corresponds to the explicit understanding of the whole computation
46unit 7
How does recursion work?How does recursion work?
On the other hand, in order to understand a recursive method we don’t have to follow the details of the computation process:
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
How is 5! computed? - simply, we call factorial(4) to compute 4!, and multiply the result by 5
47unit 7
How does recursion work?How does recursion work?
and how is 4! computed? when trying to understand a recursive solution, it’s best to avoid thinking this way
Instead, assuming that the method works for smaller inputs, we try to understand how it uses this assumption to solve the problem with the current input; this + mathematical induction is sufficient
We will explain next the process by which the computer executes a recursive method
48unit 7
Recursion: implementationRecursion: implementation
When we execute a Java program, the virtual machine creates a thread that processes the program code
The thread begins in the first statement of the main() method of our program, and advances according to the flow of the program until it reaches the end of main()
The thread is allocated a section of memory that is called its Runtime Stack
49unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( ) args =
50unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( ) args =
n = 4
51unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( )
args =
input =
n = 4
52unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( )
args =
input =
n = 4
n = 4
factorial( )
53unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( )
args =
input =
n = 3
n = 4
n = 4
factorial( )
factorial( )
54unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( )
args =
input =
n = 3
n = 4
n = 4
factorial( )
factorial( )
factorial( ) n = 2
55unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( )
args =
input =
n = 3
n = 4
n = 4
factorial( )
factorial( )
factorial( ) n = 2
factorial( ) n = 1
56unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( )
args =
input =
n = 3
n = 4
n = 4
factorial( )
factorial( )
factorial( ) n = 2 1
57unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( )
args =
input =
n = 3
n = 4
n = 4
factorial( )
factorial( ) 2
58unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( )
args =
input =
n = 4
n = 4
factorial( ) 6
59unit 7
Recursion: Runtime StackRecursion: Runtime Stack
public static long factorial(int n) {
if (n==1) {
return 1;
}
return factorial(n-1)*n;
}
public static void main(String[] args) {
int n = EasyInput.readInt(“N:”);
String input =
EasyInput.readString();
System.out.println(n+”! = “+
factorial(n));
}
main( )
args =
input =
n = 4 24