Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! ==...

41
Recursion Recursion Self-Referencing Functions Self-Referencing Functions

Transcript of Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! ==...

Page 1: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

RecursionRecursion

Self-Referencing FunctionsSelf-Referencing Functions

Page 2: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

Problem # 1Problem # 1

Write a function that, given n, computes n!Write a function that, given n, computes n!

n! == 1 n! == 1 2 2 ... ... (n-1) (n-1) n n

Example:Example:

5! == 1 5! == 1 2 2 3 3 4 4 5 == 120 5 == 120

Specification:Specification:Receive:Receive:nn, an integer., an integer.Precondition: Precondition: nn >= 0 (0! == 1 && 1! == 1). >= 0 (0! == 1 && 1! == 1).Return: Return: n!n!, a double (to avoid integer overflow)., a double (to avoid integer overflow).

Page 3: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

Preliminary AnalysisPreliminary Analysis

At first glance, this is a counting problem, At first glance, this is a counting problem, so we could solve it with a for loop: so we could solve it with a for loop:

double Factorial(int n) { double result = 1.0; for (int i = 2; i <= n; i++) result *= i; return result; }

But let’s instead learn a different approach... But let’s instead learn a different approach...

Page 4: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

AnalysisAnalysis

Consider:Consider: n! n! == 1 == 1 2 2 ... ... (n-1) (n-1) nn

so:so: (n-1)!(n-1)! == 1 == 1 2 2 ... ... (n-1) (n-1)

subsituting:subsituting: n! n! == (n-1)! == (n-1)! n n

We have defined the ! function We have defined the ! function in terms of in terms of itselfitself..

Historically, this is how the function was Historically, this is how the function was defined before computers (and for-loops) defined before computers (and for-loops) existed.existed.

Page 5: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

RecursionRecursionA function that is defined in terms of itself is called A function that is defined in terms of itself is called self-referentialself-referential, or , or recursiverecursive..

Recursive functions are designed in a 3-step process:Recursive functions are designed in a 3-step process:

1. Identify a 1. Identify a base casebase case -- an instance of the problem whose solution is -- an instance of the problem whose solution is trivialtrivial..

Example: The factorial function has two base cases:Example: The factorial function has two base cases:

if n == 0: n! == 1if n == 0: n! == 1

if n == 1: n! == 1if n == 1: n! == 1

Page 6: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

Induction StepInduction Step2. Identify an 2. Identify an induction stepinduction step -- a means of solving the non-trivial (or “big”) instances of the problem using one or more “smaller” instances of the problem. -- a means of solving the non-trivial (or “big”) instances of the problem using one or more “smaller” instances of the problem.

Example: In the factorial problem, we solve the “big” problem using a “smaller” version of the problem:Example: In the factorial problem, we solve the “big” problem using a “smaller” version of the problem:

n! == n! == (n-1)! n n

3. Form an algorithm from the base case and induction step.3. Form an algorithm from the base case and induction step.

Page 7: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

AlgorithmAlgorithm

// Factorial(n)// Factorial(n)0. Receive n.0. Receive n.1. If n > 1:1. If n > 1:

Return Factorial(n-1) * n.Return Factorial(n-1) * n.ElseElse

Return 1.Return 1.

Page 8: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

DiscussionDiscussion

The scope of a function begins at its prototype The scope of a function begins at its prototype (or heading) and ends at the end of the file. (or heading) and ends at the end of the file.

Since the body of a function lies within its scope, Since the body of a function lies within its scope, nothing prevents a function from calling itself.nothing prevents a function from calling itself.

Page 9: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

CodingCoding/* Factorial(n), defined recursively * ... */

double Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1.0;}

Page 10: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Suppose the function is called with n == 4.Suppose the function is called with n == 4.

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

Page 11: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

The function starts executing, with n == 4.The function starts executing, with n == 4.

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

Page 12: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

The if executes, and n (4) > 1, ...The if executes, and n (4) > 1, ...

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

Page 13: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

and computing the return-value calls Factorial(3).and computing the return-value calls Factorial(3).

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

Page 14: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

This begins a This begins a new executionnew execution, in which n == 3., in which n == 3.

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

Page 15: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Its if executes, and n (3) > 1, ...Its if executes, and n (3) > 1, ...

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

Page 16: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

and computing its return-value calls Factorial(2).and computing its return-value calls Factorial(2).

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

Page 17: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

This begins a new execution, in which n == 2.This begins a new execution, in which n == 2.

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

n 2

return ?

Factorial(2)

Page 18: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Its if executes, and n (2) > 1, ...Its if executes, and n (2) > 1, ...

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

n 2

return ?

Factorial(2)

Page 19: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

and computing its return-value calls Factorial(1).and computing its return-value calls Factorial(1).

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

n 2

return ?

Factorial(2)

Page 20: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

This begins a new execution, in which n == 1.This begins a new execution, in which n == 1.

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

n 2

return ?

Factorial(2)

n 1

return ?

Factorial(1)

Page 21: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

The if executes, and the condition n > 1 is The if executes, and the condition n > 1 is falsefalse, ..., ...

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

n 2

return ?

Factorial(2)

n 1

return ?

Factorial(1)

Page 22: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

so its return-value is computed as 1 (the base case)so its return-value is computed as 1 (the base case)

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

n 2

return ?

Factorial(2)

n 1

return 1

Factorial(1)

Page 23: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Factorial(1) terminates, returning 1 to Factorial(2).Factorial(1) terminates, returning 1 to Factorial(2).

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

n 2

return ?

Factorial(2)

n 1

return 1

= 1 * 2

Page 24: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Factorial(2) resumes, computing its return-value:Factorial(2) resumes, computing its return-value:

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

n 2

return 2

Factorial(2)

= 1 * 2

Page 25: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Factorial(2) terminates, returning 2 to Factorial(3):Factorial(2) terminates, returning 2 to Factorial(3):

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return ?

Factorial(3)

n 2

return 2

= 2 * 3

Page 26: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Factorial(3) resumes, and computes its return-value:Factorial(3) resumes, and computes its return-value:

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return 6

Factorial(3)

= 2 * 3

Page 27: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Factorial(3) terminates, returning 6 to Factorial(4):Factorial(3) terminates, returning 6 to Factorial(4):

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return ?

Factorial(4)

n 3

return 6

= 6 * 4

Page 28: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Factorial(4) resumes, and computes its return-value:Factorial(4) resumes, and computes its return-value:

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return 24

Factorial(4)

= 6 * 4

Page 29: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

BehaviorBehavior

Factorial(4) terminates, returning 24 to its caller.Factorial(4) terminates, returning 24 to its caller.

int Factorial(int n){ if (n > 1) return Factorial(n-1) * n; else return 1;}

n 4

return 24

Factorial(4)

Page 30: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

DiscussionDiscussion

If we time the for-loop version and the If we time the for-loop version and the recursive version, the for-loop version will recursive version, the for-loop version will usually win, because the overhead of a usually win, because the overhead of a function call is far more time-consuming function call is far more time-consuming than the time to execute a loop.than the time to execute a loop.

However, there are problems where the However, there are problems where the recursive solution is more efficient than a recursive solution is more efficient than a corresponding loop-based solution.corresponding loop-based solution.

Page 31: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

Problem # 2Problem # 2

Consider the exponentiation problem:Consider the exponentiation problem:

Given two values Given two values xx and and nn, compute , compute xxnn..

Example: 3Example: 333 == 27 == 27

Specification:Specification:

Receive: Receive: xx, , nn, two numbers., two numbers.Precondition: Precondition: nn >= 0 (for simplicity) && >= 0 (for simplicity) &&

nn is a whole number. is a whole number.Return: Return: xx raised to the power raised to the power nn..

Page 32: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

AnalysisAnalysis

The loop-based solution requires The loop-based solution requires n “steps” n “steps” (trips through the loop) to solve the problem: (trips through the loop) to solve the problem:

double Power(double x, int n){ double result = 1.0;

for (int i = 1; i <= n; i++) result *= x;

return result;}

There is a faster recursive solution.There is a faster recursive solution.

Page 33: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

Analysis (Analysis (Ct’dCt’d))

How do we perform exponentiation recursively?How do we perform exponentiation recursively?

Base caseBase case::

nn == 0 == 0

Return 1.0.Return 1.0.

Page 34: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

Analysis (Analysis (Ct’dCt’d))

Induction stepInduction step: : nn > 0 > 0

We might recognize thatWe might recognize that

xxnn == == xx xx ... ... xx xx // // nn factors of factors of xxandand

xxn-1n-1 == == xx xx ... ... xx // // n-1n-1 factors of factors of xxand so perform a substitution:and so perform a substitution:

Return Power(x, n-1) * x.Return Power(x, n-1) * x.

However, this requires However, this requires n “steps” (recursive n “steps” (recursive calls), which is no better than the loop version.calls), which is no better than the loop version.

Page 35: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

Analysis (Analysis (Ct’dCt’d))

Induction-step: n > 0.Induction-step: n > 0.

Instead, we again begin withInstead, we again begin with

xxnn == == xx xx ... ... xx xx // // nn factors of factors of xxbut note thatbut note that

xxn/2n/2 == == xx ... ... xx // // n/2n/2 factors of factors of xxand then recognize that when and then recognize that when nn is even: is even:

xxnn == == xxn/2n/2 xxn/2n/2

while when while when nn is odd: is odd:

xxnn == == xx xxn/2n/2 xxn/2n/2

Page 36: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

Analysis (Analysis (Ct’dCt’d))

Induction step: n > 0.Induction step: n > 0.

We can avoid computing We can avoid computing xxn/2n/2 twice by doing it once, twice by doing it once, storing the result, and using the stored value: storing the result, and using the stored value:

a. a. Compute Compute partialResultpartialResult = Power( = Power(xx, , n/2n/2);); b. b. If If xx is even: is even:

Return Return partialResult * partialResultpartialResult * partialResult..ElseElse

Return Return x * partialResult * partialResultx * partialResult * partialResult..End if.End if.

This version is significantly faster than the loop-This version is significantly faster than the loop-based version of the function, as n gets larger.based version of the function, as n gets larger.

Page 37: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

AlgorithmAlgorithm

0. Receive 0. Receive x, nx, n..1. If 1. If nn == 0: == 0:

Return 1.0;Return 1.0;Else Else

a. Compute a. Compute partialResultpartialResult = Power( = Power(x, n/2x, n/2););b. if b. if nn is even: is even:

Return Return partialResult * partialResultpartialResult * partialResult;; elseelse

Return Return x * partialResult * x * partialResult * partialResultpartialResult;;

end if.end if.

Page 38: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

CodingCoding

We can then code this algorithm as follows:We can then code this algorithm as follows:

double Power(double x, int n){ if (n == 0) return 1.0; else { double partialResult = Power(x, n/2);

if (n % 2 == 0) return partialResult * partialResult; else return x * partialResult * partialResult; }}

which is quite simple, all things considered...which is quite simple, all things considered...

Page 39: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

How Much Faster?How Much Faster?

How many “steps” (recursive calls) in this How many “steps” (recursive calls) in this version?version?

Power(x, Power(x, ii)) ii Steps Steps StepsSteps (loop version)(loop version) (this (this

version)version)Power(x, 0)Power(x, 0) 00 0 0 0 0Power(x, 1)Power(x, 1) 11 1 1 1 1Power(x, 2)Power(x, 2) 22 2 2 2 2Power(x, 4)Power(x, 4) 44 4 4 3 3Power(x, 8)Power(x, 8) 88 8 8 4 4Power(x, 16)Power(x, 16) 1616 16 16 5 5Power(x, 32)Power(x, 32) 3232 32 32 6 6......Power(x, Power(x, nn)) nn nn log2(n)+1

Page 40: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

How Much Faster? (How Much Faster? (Ct’dCt’d))

The larger The larger nn is, the better this version performs: is, the better this version performs:

Power(x, Power(x, ii)) ii Steps Steps StepsSteps (loop version)(loop version) (this (this

version)version)Power(x, 1024)Power(x, 1024) 10241024 1024 1024 11 11Power(x, 2048)Power(x, 2048) 20482048 2048 2048 12 12Power(x, 4096)Power(x, 4096) 40964096 4096 4096 13 13Power(x, 8192)Power(x, 8192) 81928192 8192 8192 14 14......

The The obviousobvious way to solve a problem may not be way to solve a problem may not be the the most efficientmost efficient way! way!

Page 41: Recursion Self-Referencing Functions. Problem # 1 Write a function that, given n, computes n! n! == 1 2... (n-1) n n! == 1  2 ...  (n-1)  nExample:

SummarySummary

A function that is defined in terms of itself is A function that is defined in terms of itself is called a called a recursiverecursive function. function.

To solve a problem recursively, you must be able To solve a problem recursively, you must be able to identify a to identify a base casebase case, and an , and an induction stepinduction step..

Determining how efficiently an algorithm solves Determining how efficiently an algorithm solves a problem is an area of computer science a problem is an area of computer science known as known as analysis of algorithmsanalysis of algorithms..

Analysis of algorithms measures the time of an Analysis of algorithms measures the time of an algorithm in terms of abstract “steps”, algorithm in terms of abstract “steps”, as opposed to specific statements. as opposed to specific statements.