Introduction to Recursion
by
Dr. Bun YueProfessor of Computer Science
[email protected]://sce.uhcl.edu/yue/
2013
CSCI 3333 Data Structures
Acknowledgement
Mr. Charles Moen Dr. Wei Ding Ms. Krishani Abeysekera Dr. Michael Goodrich
Recursion
Recursion is a way to combat complexity and solve problems.
Solving a problem by: solving the problem for ‘trivial’ cases,
and solving the same problem, but with
smaller sizes.
Recursive Definition
A recursive definition: a definition in terms of itself.
Example: 0 is a natural number. If x is a natural number, then x+1 is
a natural number. (or x is a natural number if x-1 is a natural number).
Mathematical Induction
Proof by Induction is recursive in nature.
To prove S(n) for all n = 1, 2, 3. Show:
1. S(n) is true for n = 1.2. If S(k) is true for all k from 1 to n-1,
then S(n) is true
Recursive Methods
A recursive method may call itself.Example: factorial.Iterative definition:n! = n . (n-1) . (n-2) … 3 . 2 . 1
Recursive definition:
elsenfn
nnf
)1(
0 if1)(
Factorial Implementation: Fac-1
Found here in C++:
int factorial(int number) { int temp;
if (number <= 1) return 1; temp = number * factorial(number - 1); return temp;
}
What do you think?
Another Implementation: Fac-2
// recursive factorial function in Java: public static int recursiveFactorial(int n) { if (n == 0) return 1; // base case else return n *
recursiveFactorial(n- 1);// recursive case
} // By Goodrich
What do you think?
Content of a Recursive Method
Base case(s): exit conditions Values of the input variables (exit
conditions) for which we perform no recursive calls are called base cases (there should be at least one base case).
Every possible chain of recursive calls must eventually reach a base case.
Recursive calls: recursive conditions Calls to the current method. Each recursive call should be defined so
that it makes progress towards a base case.
Visualizing Recursion
Recursion trace A box for each
recursive call An arrow from each
caller to callee An arrow from each
callee to caller showing return value
Example recursion trace:
recursiveFactorial (4)
recursiveFactorial (3)
recursiveFactorial (2)
recursiveFactorial (1)
recursiveFactorial (0)
return 1
call
call
call
call
return 1*1 = 1
return 2*1 = 2
return 3*2 = 6
return 4*6 = 24 final answercall
Tips on Recursive Analysis
Identify exit and recursive conditions.
Ensure that the conditions cover all input ranges.
Input Range Analysis of Fac
Fac-1 and Fac-2: n (or number) has the type of int.
Fac-2 may be caught in circularity forever for negative numbers. This is especially serious in Java as it
does not support unsigned int.
Complexity Analysis
To analyze the time complexity of a recursive routine, a recurrence relation may need to be identified and solved.
E.g. for factorial: T(0) = dT(N) = T(N-1) + cSolving by substitution: T(N) = cN + d
= O(N)
Reversing an Array
Algorithm ReverseArray(A, i, j): Input: An array A and nonnegative
integer indices i and j Output: The reversal of the elements in
A starting at index i and ending at j
if i < j thenSwap A[i] and A[ j]ReverseArray(A, i + 1, j - 1)
end if; return;
Tips on Constructing Recursion
In creating recursive methods, it is important to define the methods in ways that facilitate recursion.
This sometimes requires that we may define additional parameters that are passed to the method.
For example, we defined the array reversal method as ReverseArray(A, i, j), not ReverseArray(A).
Implementation in Java
public static void arrayReversal (int[] a) {arrayReversalWithRange(a, 0, a.length-1);
}
private static void arrayReversalWithRange(int[] a, int i, int j) {if (i < j){ int temp = a[i];
a[i] = a[j];a[j] = temp;arrayReversalWithRange(a,i+1,j-1);
}}
Calling arrayReversal
public static void main (String args[]) {
int b[] = {1,2,3,4,5,6,7}; arrayReversal(b); for (int i=0; i<b.length; i++) { System.out.println(b[i] + " "); }
}
Computing Powers
The power function, p(x,n)=xn, can be defined recursively:
This leads to an power function that runs in linear time (for we make n recursive calls).
We can do better than this, however.
else)1,(
0 if1),(
nxpx
nnxp
Recursive Squaring We can derive a more efficient linearly
recursive algorithm by using repeated squaring:
For example,24 = 2(4/2)2 = (24/2)2 = (22)2 = 42 = 1625 = 21+(4/2)2 = 2(24/2)2 = 2(22)2 = 2(42) = 3226 = 2(6/ 2)2 = (26/2)2 = (23)2 = 82 = 6427 = 21+(6/2)2 = 2(26/2)2 = 2(23)2 = 2(82) = 128.
even is 0 if
odd is 0 if
0 if
)2/,(
)2/)1(,(
1
),(2
2
x
x
x
nxp
nxpxnxp
A Recursive Squaring Method
Algorithm Power(x, n): Input: A number x and integer n Output: The value xn
if n = 0 thenreturn 1
if n is odd theny = Power(x, (n - 1)/ 2)return x · y · y
elsey = Power(x, n/ 2)return y · y
Analyzing the Recursive Squaring Method
Algorithm Power(x, n): Input: A number x and integer n Output: The value xn
if n = 0 thenreturn 1
if n is odd theny = Power(x, (n - 1)/ 2)return x · y · y
elsey = Power(x, n/2)return y · y It is important that we
used a variable twice here rather than calling the method twice.
Each time we make a recursive call we halve the value of n; hence, we make log n recursive calls. That is, this method runs in O(log n) time.
Further Analysis
Need to limit the range of n, or. Add the recursive condition:
if n < 0 return 1 / Power(x, -n);
Tail Recursion 1 Tail recursion occurs when a linearly
recursive method makes its recursive call as its last step.
The array reversal method is an example.
Such methods can easily be converted to non-recursive methods (which saves on some resources), often by the compiler and runtime system.
Tail Recursion Example
Algorithm IterativeReverseArray(A, i, j ): Input: An array A and nonnegative integer
indices i and j Output: The reversal of the elements in A
starting at index i and ending at j while i < j do
Swap A[i ] and A[ j ]i = i + 1j = j - 1
return
Tail Recursion 2
Tips: for linear recursion, write your method using tail recursion.
Example: Fac-1 does not use tail recursion. Fac-2 does.
Binary Recursion Binary recursion occurs whenever there are
two recursive calls for each non-base case. Example: the DrawTicks method for drawing
ticks on an English ruler.
A Binary Recursive Method for Drawing Ticks
// draw a tick with no labelpublic static void drawOneTick(int tickLength) { drawOneTick(tickLength, - 1); }
// draw one tickpublic static void drawOneTick(int tickLength, int tickLabel) { for (int i = 0; i < tickLength; i++) System.out.print("-"); if (tickLabel >= 0) System.out.print(" " + tickLabel); System.out.print("\n");}public static void drawTicks(int tickLength) { // draw ticks of given length if (tickLength > 0) { // stop when length drops to 0 drawTicks(tickLength- 1); // recursively draw left ticks drawOneTick(tickLength); // draw center tick drawTicks(tickLength- 1); // recursively draw right ticks }}public static void drawRuler(int nInches, int majorLength) { // draw ruler drawOneTick(majorLength, 0); // draw tick 0 and its label for (int i = 1; i <= nInches; i++) { drawTicks(majorLength- 1); // draw ticks for this inch drawOneTick(majorLength, i); // draw tick i and its label }}
Note the two recursive calls
Binary Recursion
Binary Recursions are expensive. If possible, convert it to linear
recursion.
Fibonacci Numbers
Fibonacci numbers are defined recursively:
F0 = 0
F1 = 1
Fi = Fi-1 + Fi-2 for i > 1.
Recursive Fibonacci Algorithm
Algorithm BinaryFib(k): Input: Nonnegative integer k
Output: The kth Fibonacci number Fk
if k = 1 thenreturn k
elsereturn BinaryFib(k - 1) + BinaryFib(k - 2)
Binary recursion from Goodrich’s book. Any problem here?
A C++ Implementation
long fib(unsigned long n) { if (n <= 1) { return n; } else { return fib(n-1)+fib(n-2); }}
Fibonacci Number
Negative Fibonacci Number: F-1 = 1,
F-2 = -1,
Fn = F(n+2)−F(n+1).
1,-1,2,-3,5,-8,13,…
Analyzing the Binary Recursion Fibonacci Algorithm
Let nk denote number of recursive calls made by BinaryFib(k). Then n0 = 1 n1 = 1 n2 = n1 + n0 + 1 = 1 + 1 + 1 = 3 n3 = n2 + n1 + 1 = 3 + 1 + 1 = 5 n4 = n3 + n2 + 1 = 5 + 3 + 1 = 9 n5 = n4 + n3 + 1 = 9 + 5 + 1 = 15 n6 = n5 + n4 + 1 = 15 + 9 + 1 = 25 n7 = n6 + n5 + 1 = 25 + 15 + 1 = 41 n8 = n7 + n6 + 1 = 41 + 25 + 1 = 67.
Note that the value at least doubles for every other value of nk. That is, nk > 2k/2. It is exponential!
A Better Fibonacci Algorithm
Use linear recursion instead:Algorithm LinearFibonacci(k): Input: A nonnegative integer k Output: Pair of Fibonacci numbers (Fk, Fk-1) if k = 1 then
return (k, 0) else
(i, j) = LinearFibonacci(k - 1)return (i +j, i)
Runs in O(k) time.
Implementation
Java and C++ do not return multiple values.
Perl’s implementation:
sub fib {return (1,0) if $_[0] == 1;my ($i, $j) = fib($_[0]-1);return ($i+$j, $i);
}
print "fib(13): " + fib(13) + "\n";
Java implementation
public static int Fab(int n) {if (n <= 2) return 1;return FabHelper(n,2,1,1);
}
private static int FabHelper(int n, int k, int fk, int fk_1) {
if (n == k) return fk;else return FabHelper(n, k+1, fk + fk_1, fk);
}
Questions and Comments?
Top Related