Recursion Chapter 12
Outline What is recursion Recursive algorithms with simple variables Recursion and arrays Recursion and complexity proof by induction
What is Recursion Recursion is a kind of “Divide & Conquer” Divide problem into smaller problems Solve the smaller problems Recursion Divide problem into smaller versions of itself Smallest version(s) can be solved directly
Coding Recursively Remember the two imperatives: SMALLER! STOP! when you call the method inside itself, one of the arguments has to be smaller than it was STOP! if the argument that gets smaller is very small (usually 0 or 1), then don’t do the recursion
Recursive Countdown Print the numbers from N down to 1 recursive method (stop when N is zero) public static void countDownFrom(int n) { if (n > 0) { // STOP if n == 0! System.out.print(n + " "); countDownFrom(n - 1); // SMALLER! } to count down from 10: print 10; count down from 9 …. 10 9 8 7 6 5 4 3 2 1
Recursive Countdown Print the numbers from N down to 1 recursive method (stop when N is zero) public static void countDownFrom(int n) { if (n > 0) { // STOP if n == 0! System.out.print(n + " "); countDownFrom(n - 1); // SMALLER! } to count down from 9: print 9; count down from 8…. 10 9 8 7 6 5 4 3 2 1
Recursive Countdown Print the numbers from N down to 1 recursive method (stop when N is zero) public static void countDownFrom(int n) { if (n > 0) { // STOP if n == 0! System.out.print(n + " "); countDownFrom(n - 1); // SMALLER! } to count down from 0: (do nothing) 10 9 8 7 6 5 4 3 2 1
Recursive Functions Function defined in terms of itself one or more STOPs (“base case(s)”) one or more SMALLERs (“recursive case(s)”) n! = 1 if n == 0 n*(n-1)! otherwise Fib(n) = 1 if n == 0 1 if n == 1 Fib(n–1)+Fib(n–2) otherwise
The Factorial Method Product of numbers from N down to 1 recursive method public static int factorial(int n) { if (n > 0) { return n * factorial(n - 1); // smaller } else { return 1; // stop }
Getting Smaller 4! = 4 * 3! Recursive Case 4 * 6 = 24 0! = 1 Base Case Base Case n! = 1 if n == 0 n*(n-1)! otherwise Recursive Case
Calling a Recursive Function Just like calling any other function System.out.println(factorial(5)); The function returns the factorial of what you give it because that’s what it does it returns the factorial every time you call it including when you call it inside its definition
Towers of Hanoi // ----- s == start, f == finish, x == extra ----- // public static void hanoi(int n, char s, char f, char x) { if (n > 0) { // stop if n == 0 hanoi(n - 1, s, x, f); // smaller from start to extra System.out.println("Move a disk from " + s + " to " + f + "."); hanoi(n - 1, x, f, s); // smaller from extra to finish }
Recursion with Arrays Simple recursion Recursion with arrays Values get smaller Hanoi(64) calls Hanoi(63) Hanoi(63) calls Hanoi(62) Recursion with arrays Array length gets smaller Look at less of the array
Example Array Recursion To Print an Array in reverse Base Case (Stop) If the array has length zero, do nothing Recursive Case (Smaller) First print the last element of the array Then print the rest of the array in reverse 6 100 3 -2 8 18 5 5 18 8 -2 3 100 6 5
Print Array in Reverse Give “length” of array to print, too reduce “length” by 1 until get to 0 NOTE: we’re just pretending it’s smaller public static void printInReverse(int[] arr, int len) { if (len > 0) { // stop if len == 0 System.out.print(arr[len - 1] + " "); printInReverse(arr, len - 1); // “smaller” array }
Another Example To Find the Maximum Value in an Array Base Case if length is 1, the only element is the maximum Recursive Case Get the maximum from the rest of the array... ...& compare it to the last element Return the bigger 6 100 3 -2 8 18 5 100 5 100
Remember to use a “pretend” length Exercise Translate the (recursive) algorithm from the previous slide into Java Base Case if length is 1, the only element is the maximum Recursive Case Get the maximum from the rest of the array... ...& compare it to the last element Return the bigger Remember to use a “pretend” length
Working From Both Ends Sometimes we want to be able to shorten the array at either end Pass start and end points instead of length Done when lo > hi (for len==0), or lo == hi (for len==1) “Sub-Array processing”
Working From Both Ends Alternate way to find array maximum compare first and last elements drop the smaller out of range we’re using stop when array has only one element left maximum is that one element 6 100 3 -2 8 18 5 100
Working From Both Ends Alternate way to find array maximum public static int maximum(int[] arr, int lo, int hi) { if (lo == hi) { return arr[lo]; // stop! } else if (arr[lo] > arr[hi]) { return maximum(arr, lo, hi - 1); // smaller } else { return maximum(arr, lo + 1, hi); // smaller }
Array Splitting Sub-array processing can get rid of more than one element at a time Binary split is a common method do top “half” and bottom “half” separately combine to get answer 6 100 3 -2 8 18 5 100 18 100
Array Splitting Alternate way to find array maximum public static int maximum(int[] arr, int lo, int hi) { if (lo == hi) { return arr[lo]; // stop! } else { int mid = lo + (hi - lo) / 2; int maxLo = maximum(arr, lo, mid); // smaller! int maxHi = maximum(arr, mid+1, hi); // smaller! return Math.max(maxLo , maxHi); }
Defensive Programming Consider finding the midpoint mid = lo + (hi – lo) / 2; could just do (hi + lo) / 2 BUT what if the array is HUGE hi == 2,000,000,000; lo == 1,000,000,000 (hi + lo) / 2 == -647,483,648 (int overflow) lo + (hi – lo) / 2 == 1,500,000,000 (no overflow) hi and lo both positive, so no underflow worry
Array Recursion Exercise Given an array and a number, find out if the number is in the array (contains method) NOTE: the array is unsorted Base Case(s) ? Recursive Case(s)
Recursive Algorithm Analysis Still in terms of size of problem size of n for factorial(n), fibonacci(n), … size of array/linked structure in printInReverse, findMaximum, … Base case probably just one operation Recursive case recursive count amount of work for n in terms of amount of work for n - 1
Work for printInReverse (Array) N is the “length” of the array (len) public static void printInReverse(int[] arr, int len) { if (len > 0) { // stop if len == 0 System.out.print(arr[len - 1] + " "); printInReverse(arr, len - 1); // “smaller” array } if len == 0: compare len to 0: W(0) = 1 if len > 0: compare len to 0, print arr[len-1], call printInReverse with len-1: W(len) = 2 + W(len - 1)
Recurrence Relation When W(n) defined in terms of W(n – 1)… or some other smaller value than n … it’s called a recurrence relation It’s a recursive definition of W has a base case (n = 0 or n = 1 or …) has a recursive case public static int W(int n) { if (n == 0) return 1; else return 2 + W(n – 1); }
Solving by Inspection Work for printInReverse: W(0) = 1 (change) W(1) = 2 + W(0) = 3 +2 W(2) = 2 + W(1) = 5 +2 W(3) = 2 + W(2) = 7 +2 W(4) = 2 + W(3) = 9 +2 … linear: factor of 2 W(N) = 2 + W(N–1) = 2N + 1?
Solving by Inspection Table with N and Work for N (W) calculate change in work (ΔW) for each step N W recurrence ΔW 1 3 W(0) + 2 2 5 W(1) + 2 7 W(2) + 2 4 9 W(3) + 2 11 W(4) + 2 ?
Solving by Inspection: Linear Change in W will be a constant > 0 probably: W(N) = (ΔW)N + W(0) N W recurrence ΔW 1 3 W(0) + 2 2 5 W(1) + 2 7 W(2) + 2 4 9 W(3) + 2 11 W(4) + 2 2N + 1
Solving by Inspection: Quadratic Add column for change in ΔW Δ(ΔW) constant W(N) = (Δ(ΔW)/2)N2 + … N2 1 4 9 16 25 N W recurrence ΔW recurrence(2) Δ(ΔW) 1 3 W(0) + 2 2 7 W(1) + 4 4 ΔW(1) + 2 13 W(2) + 6 6 ΔW(2) + 2 21 W(3) + 8 8 ΔW(3) + 2 5 31 W(4) + 10 10 ΔW(4) + 2 N2 + ? + ? N2 + N + 1 W(N-1) + 2N 2N
Proving Your Formula You only looked at a few values will the formula you got work on all values? Can prove that a formula will work proof by induction Assume it works up to an arbitrary N – 1 use recurrence relation to show it works for N proves it works up to any N
Proof by Induction (Linear) Suppose W(n) = 2n + 1 for n < N What is W(N)? W(N) = W(N – 1) + 2 (recurrence relation) but N – 1 < N, so: W(N – 1) = 2(N – 1) + 1 = 2N – 2 +1 = 2N – 1 W(N) = (2N – 1) + 2 = 2N + 1 so W(N) also = 2N + 1 and that’s for any N > 0
Proof by Induction (Quadratic) Suppose W(n) = n2 + n + 1 for n < N What is W(N)? W(N) = W(N – 1) + 2N (recurrence relation) but N – 1 < N, so: W(N – 1) = (N – 1)2 + (N – 1) + 1 = N2 – N + 1 W(N) = (N2 – N + 1) + 2N = N2 + N + 1 so W(N) also = N2 + N + 1 and that’s for any N > 0
Exercise Given recurrence reln W(N) = W(N-1) + 4, prove that W(N) = 4N + 5 Given W(N) = W(N-1) + 2N + 1, prove that W(N) = N2 + 2N
Analyzing Array Splitting Split array into two equal(ish) pieces easiest to analyze if N is a power of 2 1, 2, 4, 8, 16, … or one less than that (if middle item removed) 0, 1, 3, 7, 15 makes exactly equal splits Need to factor in change in N
Analyzing Array Splitting Work when splitting exactly find maximum using array splitting W(8) = 2W(4) W(2N) = 2W(N) 6 100 3 -2 8 18 5 35 W(8) W(4) W(4) W(2) W(2)
Array Splitting public static int maximum(int[] arr, int lo, int hi) { if (lo == hi) { // 1 comparison return arr[lo]; // 1 array access } else { int mid = lo + (hi - lo) / 2; // 3 math ops int maxLo = maximum(arr, lo, mid); // 1 asgn + REC int maxHi = maximum(arr, mid+1, hi); // 1 asgn + REC return (maxLo > maxHi) ? maxLo : maxHi; // 1 comparison } W(1) = 2 DW / DN W(2) = 7 + 2W(1) = 7 + 2(2) = 11 + 9 / +1 9N – 7 W(4) = 7 + 2W(2) = 7 + 2(11) = 29 +18 / +2 9N – 7 W(8) = 7 + 2W(4) = 7 + 2(29) = 65 +36 / +4 9N – 7 W(16) = 7 + 2W(8) = 7 + 2(65) = 137 +72 / +8 9N – 7
findMaximum by Splitting Split array into two (nearly equal) parts look at only powers of 2 even splits all the way down looks like W(N) = 9N – 7 linear works for N <= 16 do induction on 2N instead of N+1 (later we’ll do 2N+1)
Proof by Induction (Part 1) Assume W(n) = 9n – 7 for n < 2N What’s W(2N)? W(2N) = 7 + 2W(N) but N < 2N, so W(N) = 9N – 7… … so W(2N) = 7 + 2(9N–7) = 7 + 18N – 14 = 18N – 7 = 9(2N) – 7 same formula works for all even N
Proof by Induction (Part 2) Assume W(n) = 9n – 7 for n < 2N What’s W(2N + 1)? W(2N + 1) = 7 + W(N) + W(N+1) but N < N+1 < 2N, so … W(2N + 1) = 7 + (9N – 7) + (9(N+1) – 7) = 7 + 9N – 7 + 9N + 9 – 7 = 18N + 9 – 7 = 9(2N+1) – 7 same formula works for all odd N
Towers of Hanoi N = number of disks Work = number of moves W(0) = 0 (change) W(1) = W(0) + 1 + W(0) = 1 +1 W(2) = W(1) + 1 + W(1) = 3 +2 W(3) = W(2) + 1 + W(2) = 7 +4 W(4) = W(3) + 1 + W(3) = 15 +8 W(5) = W(4) + 1 + W(4) = 31 +16
Solve by Inspection Work doubles at each step of N sounds exponential compare work with 2N 2N = W(N) + 1 so work = 2N – 1 exponential N W(N) 2N 1 2 3 4 7 8 15 16 5 31 32 2N – 1
Proof by Induction Assume W(n) = 2n – 1 for n < N What’s W(N)? W(N) = 1 + 2W(N–1) = 1 + 2(2N–1 – 1) = 1 + 2N – 2 = 2N – 1 formula works for all N
Exercise How much work is done by printInReverse for the linked structure? private void printInReverse(Node first) { if (first != null) { // stop if list is empty! printInReverse(first.next); // smaller! System.out.println(first.data + " "); } write recurrence relation solve order of magnitude
Next Time Faster sorting methods