CS111 Computer Programming Department of Computer Science Wellesley College Classic Recursion Examples Plus Class Methods and Applications Tuesday, October 23, 2007
Classic Recursion13-2 Goals for Today 1. Present some classic recursion examples that every computer scientist is expected to know: factorial, fibonacci, and Towers of Hanoi. 2. Introduce tail recursion = recursion without pending operations. 3. Introduce some aspects of Java along the way that are helpful for the above: Class (static) methods Strings and concatenation Applications with command-line input Using System.out.println to trace method calls.
Classic Recursion13-3 Class ( static ) Methods A class method (marked by the keyword static) is a method whose behavior does not depend on a receiver instance. Here are some examples from the Math class (which does not have any constructor methods or instances): public static int abs (int a); Returns the absolute value of a public static int max (int a, int b); Returns the greater of a and b. public static double max (double a, double b); Returns the greater of a and b. // Similar for min public static int round (double a); Returns the integer closest to a. public static double sqrt (double a); Returns the square root of a. Class methods invoked using a class name to the left of the dot: Math.abs(-3) // evaluates to 3 Math.max(4,7) // evaluates to 7 Math.max(5.1,4.2) // evaluates to 5.1 Math.round(5.1) // evaluates to 5 Math.sqrt(2.0) // evaluates to
Classic Recursion13-4 Defining your Own Class Methods public class IntFunctions { public static int square (int x) { return x*x; } public static int sumOfSquares (int a, int b) { return IntFunctions.square(a) + IntFunctions.square(b); // could write square(a) instead of IntFunctions.square(a) } // E.g. : // IntFunctions.square(3) evaluates to 9 // IntFunctions.sumOfSquares(3,4) evaluates to 25 }
Classic Recursion13-5 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land
Classic Recursion13-6 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( a ) + IntFunctions.square( b ); 3 a 4 b
Classic Recursion13-7 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( b ); 3 a 4 b
Classic Recursion13-8 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( b ); 3 a 4 b IntFunctions.square(3) return x * x ; 3 x
Classic Recursion13-9 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( b ); 3 a 4 b IntFunctions.square(3) return 3 * 3 ; 3 x
Classic Recursion13-10 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( b ); 3 a 4 b IntFunctions.square(3) return 9 ; 3 x
Classic Recursion13-11 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( 4 ); 3 a 4 b IntFunctions.square(3) return 9 ; 3 x 9
Classic Recursion13-12 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( 4 ); 3 a 4 b IntFunctions.square(3) return 9 ; 3 x 9 IntFunctions.square(4) return x * x ; 4 x
Classic Recursion13-13 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( 4 ); 3 a 4 b IntFunctions.square(3) return 9 ; 3 x 9 IntFunctions.square(4) return 16 ; 4 x
Classic Recursion13-14 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( 4 ); 3 a 4 b IntFunctions.square(3) return 9 ; 3 x 9 IntFunctions.square(4) return 16 ; 4 x 16
Classic Recursion13-15 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( 4 ); 3 a 4 b IntFunctions.square(3) return 9 ; 3 x 9 IntFunctions.square(4) return 16 ; 4 x 16 25
Classic Recursion13-16 Class Methods in a JEM In a JEM, a class method has no receiver object or this variable. E.g.: System.out.println(IntFunctions.sumOfSquares(3,4)); Execution Land IntFunctions.sumOfSquares(3,4) return IntFunctions.square( 3 ) + IntFunctions.square( 4 ); 3 a 4 b IntFunctions.square(3) return 9 ; 3 x 9 IntFunctions.square(4) return 16 ; 4 x 16 25
Classic Recursion13-17 Strings and String Concatenation A string is a sequence of characters. Java strings can be written as characters delimited by double quotes: “cat” “Computer Science” “CS111 rocks!” In Java, strings are concatenated with + : “CS” + “111” + “ ” + “rocks!” // evaluates to “CS111 rocks!” Strings can be displayed in the console using System.out.println: System.out.println(“cat”); // displays cat System.out.println(“CS” + “111” + “ ” + “rocks!” ) // displays CS111 rocks!
Classic Recursion13-18 Strings of Digits vs. Numbers Strings of digits are different from numbers: // evaluates to 345 “111” + “234” // evaluates to “111234” Can convert between strings of digits and numbers: Integer.toString(111) + Integer.toString(234) // evaluates to “111234” Integer.parseInt(“111”) + Integer.parseInt(“234”) // evaluates to 345 Integer.parseInt(“12c”) // aborts the program with an exception If one argument of + is a string, Java will automatically convert the other: “111” // evaluates like “111” + Integer.toString(234) -> “111234” “234” // evaluates like Integer.toString(111) + “234” -> “111234” This facilitates displaying information from a program. E.g.: int a = 5; int b = 3; System.out.println( “a = ” + a + “; b = ” + b + “; Math.min(a,b) = ” + Math.min(a,b) + “;”) // displays a = 5; b = 3; Math.min(a,b) = 3;
Classic Recursion13-19 A Simple Java Application An application is a Java class that can be invoked from the top-level system, such as the Dr. Java Interaction Pane, by typing java. This invokes the class method named main() within the class. public class IntTest1 { public static void main (String[] args) { int a = 5; int b = 3; System.out.println("Math.min(" + a + ", " + b + ") = " + Math.min(a,b)); System.out.println("IntFunctions.square(" + a + ") = " + IntFunctions.square(a)); System.out.println("IntFunctions.sumOfSquares(" + a + ", " + b + ") = " + IntFunctions.sumOfSquares(a,b)); } boilerplate header for main() > java IntTest1 Math.min(5, 3) = 3 IntFunctions.square(5) = 25 IntFunctions.sumOfSquares(5, 3) = 34 Dr. Java Interaction Pane
Classic Recursion13-20 A Java Application with Arguments The args parameter in the main() method denotes an array of string arguments passed in from the top-level system: args[0] is the first string argument, args[1] is the second string argument, and so on. public class IntTest2 { public static void main (String[] args) { int a = Integer.parseInt(args[0]); int b = Integer.parseInt(args[1]); System.out.println("Math.min(" + a + ", " + b + ") = " + Math.min(a,b)); System.out.println("IntFunctions.square(" + a + ") = " + IntFunctions.square(a)); System.out.println("IntFunctions.sumOfSquares(" + a + ", " + b + ") = " + IntFunctions.sumOfSquares(a,b)); } boilerplate header for main() > java IntTest Math.min(-3, 10) = -3 IntFunctions.square(-3) = 9 IntFunctions.sumOfSquares(-3, 10) = 109 Dr. Java Interaction Pane In this case, args is the array: 01 “-3”“10”
Classic Recursion13-21 Classic Recursion: Factorial 4!=4 x (3 x 2 x 1) =4 x 3! 2!=2 x (1) =2 x 1! 1!=1 X 0! 3!=3 x (2 x 1) =3 x 2! Likewise 0!=1 base case recursive cases
Classic Recursion13-22 Factorial in Java public static int fact (int n) { } So, if n==4, fact(4) 4*fact(3) 3*fact(2) 2*fact(1) 1*fact(0)
Classic Recursion13-23 A Factorial Application public class Factorial { public static int fact (int n) { if (n == 0) { return 1; } else { return n * fact (n - 1); } public static void main(String[] args) { int n = Integer.parseInt(args[0]); System.out.println("fact(" + n + ") = " + fact(n)); }
Classic Recursion13-24 A Factorial Application with Tracing public class FactorialTrace { public static int fact (int n) { System.out.println("Entering fact(" + n + ")"); int result; if (n == 0) { result = 1; } else { result = n * fact (n - 1); } System.out.println(”Exiting fact(" + n + ") with ” + result); return result; } public static void main(String[] args) { int n = Integer.parseInt(args[0]); System.out.println("fact(" + n + ") = " + fact(n)); }
Classic Recursion13-25 What does java FactorialTrace 3 Display? > java FactorialTrace 3
Classic Recursion13-26 Tail Recursion A recursive method is tail-recursive when it has one recursive subproblemand no pending operations. In this case, conquering the subproblem solves the whole problem. Which of the following Buggle methods is tail-recursive? Why? public void bagelForward(int n) { if (n == 0) { // do nothing } else { dropBagel(); forward(); bagelForward(n-1); } public void bagelLine(int n) { if (n == 0) { // do nothing } else { dropBagel(); forward(); bagelLine(n-1); backward(); } public void bagelsToWallAndBack() { if (isFacingWall()) { dropBagel(); } else { dropBagel(); forward(); bagelsToWallAndBack(); backward(); } public void bagelsToWall() { if (isFacingWall()) { dropBagel(); } else { dropBagel(); forward(); bagelsToWall(); }
Classic Recursion13-27 Tail Recursion in TurtleWorld Which of the following Turtle methods is tail-recursive? Why? public void spiral(int steps, int angle, int length, int increment) { if (steps <= 0) { // do nothing } else { fd(length); lt(angle); spiral(steps-1, angle, length+increment, increment); } public void tree(int levels, double length, double angle, double shrink) { if (levels <= 0) { // do nothing } else { fd(length); rt(angle); tree(levels-1, length*shrink, angle, shrink); lt(2*angle); tree(levels-1, length*shrink, angle, shrink); rt(angle); bd(length); }
Classic Recursion13-28 A Tail-Recursive Factorial factTail( 4, 1 ) largest unprocessed number so far product of all numbers processed so far The desired answer factTail( 0, 24 ) * factTail( 1, 24 ) * factTail( 2, 12 ) * factTail( 3, 4 ) *
Classic Recursion13-29 Defining factTail() in Java // factorial using tail recursion public static int factTail (int num, int ans) { } serves as a place to pass answer along
Classic Recursion13-30 An Application with factTail() public class FactorialTail { public static int fact (int n) { return factTail(n, 1); // seed = initial answer = empty product = 1. } public static int factTail (int num, int ans) { if (num == 0) { return ans; } else { return factTail(num - 1, num * ans); } public static void main(String [] args) { int n = Integer.parseInt(args[0]); System.out.println("fact(" + n + ") = " + fact(n)); }
Classic Recursion13-31 An Application with factTail() and Tracing public class FactorialTailTrace { public static int fact (int n) {return factTail(n, 1);} public static int factTail (int num, int ans) { if (num == 0) { System.out.println(factTail(" + num + "," + ans + ")” + “ returns ” + ans); return ans; } else { System.out.println("factTail(" + num + "," + ans + ")"); return factTail(num - 1, num * ans); } public static void main(String [] args) { int n = Integer.parseInt(args[0]); System.out.println("fact(" + n + ") = " + fact(n)); }
Classic Recursion13-32 java FactorialTailTrace 3 > java FactorialTailTrace 3
Classic Recursion13-33 Leonardo Pisano Fibonacci Time# Pairs
Classic Recursion13-34 The fib() Method public static int fib (int n) { // Returns pairs at time n if (n = 0 return n; } else { return fib(n-1) // pairs alive last month + fib(n-2); // newborn pairs } } fib(4) = 1= 0 fib(1)fib(0) = 1= 0 fib(1)fib(0) = 1 fib(2)fib(1)fib(3)fib(2) = 1 + = 2 + = 1 + = 3 +
Classic Recursion13-35 It gets worse fib(4) = 1= 0fib(1)fib(0) = 1= 0fib(1)fib(0) = 1fib(2)fib(1) fib(3)fib(2) = 1 + = 2 + = 1 + = 3 + = 1= 0fib(1)fib(0) = 1fib(2)fib(1) fib(3) = 1 + = 2 + fib(5)= 5 + How long would it take to calculate fib(100)?
Classic Recursion13-36 Is there a better way? Yes! fibTail ( 6, 0, 0, 1 ) current time (i) fib (i)fib (i+1) target time (n) the desired answer fibTail ( 6, 1, 1, 1 ) + +1 fibTail ( 6, 2, 1, 2 ) +1 + fibTail ( 6, 3, 2, 3 ) +1 + fibTail ( 6, 4, 3, 5 ) +1 + fibTail ( 6, 5, 5, 8 ) +1 + fibTail ( 6, 6, 8, 13 ) +1 +
Classic Recursion13-37 A Faster Fibonacci public class FibonacciFast { public static int fib (int n) { return fibTail(n, 0, 0, 1); } public static int fibTail (int n, int i, int fib_i, int fib_i_plus_1) { } public static void main (String[] args) { int n = Integer.parseInt(args[0]); System.out.println("fib(" + n + ") = " + fib(n)); }
Classic Recursion13-38 Towers of Hanoi
Classic Recursion13-39 Solving Hanoi in Java public static void main (String[] args) { int n = Integer.parseInt(args[0]); System.out.println(" "); System.out.println("Instructions for solving Hanoi(" + n + ", A, B, C):”); hanoi(n, "A”, "B”, "C”); } public static void moveDisk (int disk, String source, String dest { // Doesn't move a disk, but prints out an instruction to do so. System.out.println("Move disk " + disk + " from peg " + source + " to peg " + dest); } public static void hanoi (int n, String source, String dest,String spare) { }