unit 7 1 unit 7: recursion h recursion is a fundamental programming technique that can provide an...

59
1 unit 7 Unit 7: Recursion Unit 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 programmin g concepts object oriented programmin g topics in computer science syllabus

Post on 22-Dec-2015

213 views

Category:

Documents


0 download

TRANSCRIPT

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);

}

24unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

25unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

26unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

27unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

28unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

29unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

30unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

31unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

32unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

33unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

34unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

35unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

36unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

37unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

38unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

39unit 7

Towers of Hanoi ExampleTowers of Hanoi Example

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