# CS203 LECTURE 3 John Hurley Cal State LA. Recursion Recursion is the technique of breaking down a problem into smaller instances of the same problem.

## Presentation on theme: "CS203 LECTURE 3 John Hurley Cal State LA. Recursion Recursion is the technique of breaking down a problem into smaller instances of the same problem."— Presentation transcript:

CS203 LECTURE 3 John Hurley Cal State LA

Recursion Recursion is the technique of breaking down a problem into smaller instances of the same problem. In programming, a recursive algorithm is one that directly or indirectly invokes itself direct recursion: methodA calls methodA indirect recursion: methodA calls methodB and methodB calls methodA

Recursion Recursion is used in many common algorithms. It is also extremely important in functional programming, a long-established paradigm (way of thinking about and practicing programming) which is increasingly important today. As you will learn in CS312, recursion is equivalent to iteration. In other words, any problem you can solve with either of these two techniques, you can also solve with the other. However: a) many problems can be solved with simpler algorithms using recursion, but b) most programmers find iteration easier to think about

King of Hearts’ Algorithm 4 "Where shall I begin, please your majesty?" he asked. "Begin at the beginning," the King said gravely, "and go on till you come to the end; then stop.

Recursive Algorithm 5 doSomething() 1. If you are finished, stop. 2. Otherwise 1. Solve part of the problem 2. Run this algorithm

Recursion 6 For example, we can define the operation goHome() this way: – If you are at home, stop. – Else Take one step toward home. goHome()

Recursion Avoid infinite regression (and stack overflows) by defining a condition that indicates that the recursion has extended as far as it can, and that therefore causes termination of the recursion Recursion usually involves performing some operation on a sequence of values (for example, a list). The termination condition occurs when there are no values left to process.

Iterative (not Recursive) Factorial 8 function factorial is: input: integer n such that n >= 0 output: [n × (n-1) × (n-2) × … × 1] Iterative algorithm 1. create new variable called running_total with a value of 1 2. begin loop 1. if n is 0, exit loop 2. set running_total to (running_total × n) 3. decrement n 4. repeat loop 3. return running_total end

Recursive Factorial 9 Calculating the factorial of (a positive integer) n can be reduced to multiplying n times the factorial of n-1. function factorial: input: integer n such that n >= 0 output: [n × (n-1) × (n-2) × … × 1] 1. if n == 0, return 1 2. otherwise, return [ n × factorial(n-1) ] Note that this recursive solution has a simpler relationship to the actual definition of factorial than the iterative solution does

Recursive Factorial 10 public class Demo { public static int factorialRecursive(int intIn){ if(intIn ==0) return 1; return intIn * factorialRecursive(intIn -1); } public static void main(String[] args) { int num = Integer.parseInt(JOptionPane.showInputDialog(null, "Please " + "enter the number whose factorial you would like to compute")); JOptionPane.showMessageDialog(null, "The factorial of " + num + " is " + factorialRecursive(num)); }

Stack Overflow Each method call requires additional space on the call stack to track the data values for the current instance of the method If the stack exceeds the memory available to it, a stack overflow occurs public class StackOverflowDemo { int x; public static void main(String[] args){ int x = 1; StackOverflowDemo s = new StackOverflowDemo(); s.recurseToOverflow(x); } public void recurseToOverflow(int x){ System.out.println("instance " + x); x = x + 1; recurseToOverflow(x); }

Stack Overflow If you get a stack overflow error while doing your work in this class, you almost certainly have a bug in your code that involves an incorrect termination condition. Think until you figure it out and fix it. However, note that we run our programs inside Eclipse, which decides how much space to allocate to the call stack.

Euclid's Algorithm Find the GCD of two positive integers this way: Input Two positive integers, a and b. Output The greatest common divisor, g, of a and b. If a { "@context": "http://schema.org", "@type": "ImageObject", "contentUrl": "http://images.slideplayer.com/13/4035839/slides/slide_13.jpg", "name": "Euclid s Algorithm Find the GCD of two positive integers this way: Input Two positive integers, a and b.", "description": "Output The greatest common divisor, g, of a and b. If a

Euclid's Algorithm // by Robert Sedgewick and Levin Wayne : http://introcs.cs.princeton.edu/java/23recursion/Euclid.java.html // assumes p > q for simplicity public class Euclid { public static int gcd(int p, int q) {// recursive implementation if (q == 0) return p; else return gcd(q, p % q); } public static int gcd2(int p, int q) {// non-recursive implementation while (q != 0) { int temp = q; q = p % q; p = temp; } return p; } public static void main(String[] args) { int p = Integer.parseInt(JOptionPane.showInputDialog(null, "Please enter the first integer")); int q = Integer.parseInt(JOptionPane.showInputDialog(null, "Please enter the second integer")); int d = gcd(p, q); int d2 = gcd2(p, q); JOptionPane.showMessageDialog(null, "gcd(" + p + ", " + q + ") = " + d); JOptionPane.showMessageDialog(null, "gcd(" + p + ", " + q + ") = " + d2); }

Multiple Recursion A recursive problem may have more than one base case and/or more than one recursive call. The definition of the Fibonacci numbers is recursive, and the nth Fibonacci number can be found using a recursive function. Fibonacci numbers : f(n) =

Multiple Recursion public class FibonacciCalculator { public static void main(String[] args){ FibonacciCalculator f = new FibonacciCalculator(); for(int counter = 0; counter < 10; counter++){ long fib = f.fibonacci(counter); System.out.println("Fibonacci number No. " + counter + " = " + fib); } public static long fibonacci(long n) { // https://www.inf.unibz.it/~calvanese/teaching/04-05-ip/lecture-notes/uni10/node23.html if (n < 0) return -1; // F(n) is not defined when n is negative if (n == 0) return 0; else if (n == 1) return 1; else return fibonacci(n-1) + fibonacci(n-2); }

Recursion 17 Recursion can be used to process a list (or array of) values.

Recursion 18 package demos; import java.util.ArrayList; import java.util.List; //https:www.stackoverflow.com/questions/126756/examples-of-recursive-functions public class RecursionExample { public static void main(String[] args) { String[] sleeplessArray = { "ant", "frog", "goose", "weasel", "child" }; List sleeplessList = new ArrayList (); for (String s : sleeplessArray) sleeplessList.add(s); RecursionExample r = new RecursionExample(sleeplessList); } public RecursionExample(List animals){ System.out.print("There was a "); tellStory(animals); } private void tellStory(List sleeplessList) { int last=sleeplessList.size() -1; String animal = sleeplessList.get(last); if(sleeplessList.size() == 1) System.out.println("little " + animal +" who went to sleep"); else { System.out.println("little " + animal + " who couldn't go to sleep, so his mother read him a story about a "); sleeplessList.remove(last); tellStory(sleeplessList); } System.out.println("so the little " + animal + " went to sleep" ); }

Recursion 19 Recursion can also be used to process a list of values and produce a new list of processed values. This is at the heart of functional programming, as you will learn in CS332F. In the FP paradigm, neither the old list nor the objects in the list change. We produce a new list containing new values. Functional languages like LISP, Haskell, and Scala are designed to make this easy. It is less straightforward in OOP, but the new version of Java provides more support for this.

Recursion 20 package demos; import java.util.ArrayList; import java.util.List; public class ListEx { public static void main(String[] args) { List origList = new ArrayList (); for (int counter = 1; counter < 10; counter++) origList.add(counter); ListEx r = new ListEx(); List newList = r.squareList(origList, null); for(Integer i: newList) System.out.print(i + " "); } private List squareList(List oldList, List newList) { int lastIndex = oldList.size() -1; int base = oldList.get(lastIndex); oldList.remove(lastIndex); if(oldList.size() == 0) { newList = new ArrayList (); } else newList = squareList(oldList, newList); newList.add(base * base); return newList; }

Recursion 21 Here is the last example in Haskell: Function definition: squarelist :: Num a => [a] -> [a] squarelist [] = [] squarelist (x:xs) = (x*x): squarelist xs Function call: squarelist[1,2,3,4,5,6,7,8,9]

Backtracking 22 Some algorithms involve following one path as far as it can go, then backing up to the last point at which a different path could have been chosen and then following that path Depth-First Search (DFS) is a classic example. Start at the root of a tree or graph and explore as far as possible along each branch before backtracking.backtracking

Depth-First Search 23 Source of picture: https://upload.wikimedia.org/wikipedia/commons/thumb/1/1f/Depth-first-tree.svg/450px-Depth-first-tree.svg.png

File Listing Consider the problem of listing all the files in a directory, including those in all its subdirectories, etc. until every path has been exhausted. The natural way to approach this problem is to do it recursively using Depth First Search: List files in the current directory Run this algorithm on each subdirectory, continuing as far as we can go

File Listing public class FileLister { private static StringBuilder indentation = new StringBuilder(); public static void main(String args[]) { String start = JOptionPane.showInputDialog(null, "Please enter the starting directory"); getDirectoryContent(start); } private static void getDirectoryContent(String filePath) { File currentDirOrFile = new File(filePath); if (!currentDirOrFile.exists()) { return; } else if (currentDirOrFile.isFile()) { System.out.println(indentation + currentDirOrFile.getName()); return; } else { System.out.println("\n" + indentation + "|_" + currentDirOrFile.getName()); indentation.append(" "); String[] s = currentDirOrFile.list(); if (s != null) { for (String currentFileOrDirName : currentDirOrFile.list()) { getDirectoryContent(currentDirOrFile + "\\" + currentFileOrDirName); } if (indentation.length() - 3 > 3) { indentation.delete(indentation.length() - 3, indentation.length()); }

Towers Of Hanoi 26 The Towers of Hanoi problem supposes that, at the beginning of time, a group of monks in Hanoi were tasked with moving a set of 64 disks of different sizes between three pegs, according to these rules: No disk may ever be placed above a smaller disk The starting position has all the disks, in descending order of size, stacked on the first peg The ending position has all the disks in the same order, stacked on the third peg. An optimal solution for n disks requires 2 n -1 moves 2 64 = 18,446,744,073,709,551,616 There is a nice animation of a 4-disk version of the problem at https://en.wikipedia.org/wiki/File:Tower_of_Hanoi_4.gif

Towers Of Hanoi 27 Recursive solution: For any n > 1, the problem can be solved optimally in this way: Solve the problem for n -1 disks, starting at the start post and ending at the "extra" post. The remaining disk will be the largest one. Move it to the finish post. Then solve the problem for the n-1 disks, moving from the "extra" post to the finish post The above recursive procedure is applied recursively until n = 1 Before you try to understand the code from the book, try out the puzzle online at http://www.softschools.com/games/logic_games/tower_of_hanoi/ http://www.softschools.com/games/logic_games/tower_of_hanoi/

Towers Of Hanoi 28 The key to a Towers Of Hanoi solution is a recursive method like this: public void move(int disks, int from, int to) { if (disks > 0) { int other = 3 - from - to; move(disks - 1, from, other); towers[to].add(towers[from].remove()); move(disks - 1, other, to); }

Tail Recursion 29 A tail-recursive method is one in which there is one recursive call at the very end of the method, e.g. the examples above of recursive factorial calculations and Euclid's algorithm. In many programming languages, compilers can convert tail recursion into iterative code, which is more efficient since it does not create new instances on the call stack and since, in many cases, it can avoid redundant calculations. This is called Tail-Call Optimization, or TCO. Java compilers may have this feature in the future, but not at the current time.

Recursive Jokes 30 To understand recursion, read this sentence. A truly greedy man is one who writes his will and names himself as heir -- from Philogelos, the oldest known book of jokes, compiled about 400 AD. Function recurse() 1. If you are done with your program, stop 2. Otherwise, A. Try to find the problem B. Utter an obscenity 3. recurse()

Binary File I/O Real world applications usually use databases to store large amounts of information Main exceptions are apps that have unique file formats that can’t easily be translated to databases, like word processors or paint software. If you have not studied text file I/O, take a look at the material in the textbook or my lectures from CS202 (same URL as this class’ website, except for the course number.) There is a very nice way to store data from OOP applications directly to disk, though: binary file i/o

Binary File I/O Binary files store data as sequences of bytes in a format that is usually far more compact than text files. If you open a binary file in a word processor or text editor, you will see gibberish. It is possible to see the byte values in base 16 using a hex editor, but they will normally be unintelligible. Every application formats its binary files differently. The only way to easily retrieve the data is by using an application that can parse the data, normally the app that generated the file in the first place. Programming languages typically offer easy ways to store data in binary files. Java and other object-oriented languages can format objects as binary sequences for file storage. They also offer automated ways to recreate objects from the data.

Binary File I/O Output: FileOutputStream is a stream for output to a file. BufferedOutputStream is a stream with a buffer (write data in chunks for more efficiency, since there are fewer calls to the expensive input/output methods used by the operating system). BufferedOutputStream wraps an unbuffered stream such as a FileOutputStream to provide the buffer. DataOutputStream provides methods for writing primitive types as well as Strings to the stream it wraps. Methods like writeDouble() output the stated types All of these have equivalent input methods The following example stores low-level types like Double and uses parsing code when the file is read, but you will soon see an example that stores and retrieve collections of objects of programmer-defined classes.

package menudemo; // http://docs.oracle.com/javase/tutorial/essential/io/examples/DataStreams.java import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class DataStreams { static final String dataFile = "invoicedata"; static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 }; static final int[] units = { 12, 8, 13, 29, 50 }; static final String[] descs = { "Java T-shirt", "Java Mug", "Duke Juggling Dolls", "Java Pin", "Java Key Chain" }; public static void main(String[] args) throws IOException { DataOutputStream out = null; try { out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile))); for (int i = 0; i < prices.length; i ++) { out.writeDouble(prices[i]); out.writeInt(units[i]); out.writeUTF(descs[i]); } } finally { out.close(); }

DataInputStream in = null; double total = 0.0; try { in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile))); double price; int unit; String desc; try { while (true) { price = in.readDouble(); unit = in.readInt(); desc = in.readUTF(); System.out.format("You ordered %d units of %s at \$%.2f%n", unit, desc, price); total += unit * price; } } catch (EOFException e) { // this is really how Oracle's documentation suggests you find the end of a file! System.out.println("EOF reached");} System.out.format("For a TOTAL of: \$%.2f%n", total); } finally { in.close(); }

Binary I/O ObjectOutputStream and ObjectInputStream allow binary I/O of whole objects Objects read from files must be cast correctly Objects stored this way must be serializable, meaning they must implement the Serializable interface, which just identifies them as convertible to a binary stream. Serializable does not require any actual methods. If the object contains references to other objects, all the classes must be serializable! Key methods are writeObject() and readObject(). readObject() requires a cast, so you have to know what kind of objects are stored in the file you are reading.

Monster Herd Persister package demos; import java.io.Serializable; public class Monster implements Serializable { private String name; private String hometown; private String rampageBehavior; public Monster(String nameIn, String hometownIn, String rampageBehaviorIn) { name = nameIn; hometown = hometownIn; rampageBehavior = rampageBehaviorIn; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHometown() { return hometown; } public void setHometown(String hometown) { this.hometown = hometown; } public String getRampageBehavior() { return rampageBehavior; } public void setRampageBehavior(String rampageBehavior) { this.rampageBehavior = rampageBehavior; } public String toString() { return name + " from " + hometown + " likes to " + rampageBehavior; }

package monsterherd; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.File; import java.util.List; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; public class MonsterGUI { MonsterHerd herd; JFrame frame = null; JPanel panel; JMenuBar menubar; JScrollPane scrollPane; JTable table;

public MonsterGUI() { herd = new MonsterHerd(); initUI(); } public final void initUI() { frame = new JFrame(); panel = new JPanel(); table = setUpTable(); menubar = new JMenuBar(); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); final JFileChooser fc = new JFileChooser();

// enums like KeyEvent evaluate to ints createAndAddMenuItem(file, "Open", KeyEvent.VK_O, "Open File", new ActionListener() { public void actionPerformed(ActionEvent event) { int retVal = fc.showOpenDialog(frame); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fc.getSelectedFile(); herd.populateFromFile(selectedFile); updateTable(); } }); createAndAddMenuItem(file, "Add Monster", KeyEvent.VK_A, "Add Monster", new ActionListener() { public void actionPerformed(ActionEvent event) { String name = JOptionPane.showInputDialog(frame, "please enter the monster's name"); String hometown = JOptionPane.showInputDialog(frame, "please enter the monster's hometown"); String rampage = JOptionPane.showInputDialog(frame, "what does the monster do when it goes on a rampage?"); Monster m = new Monster(name, hometown, rampage); herd.addMonster(m); updateTable(); } });

createAndAddMenuItem(file, "Save", KeyEvent.VK_S, "Save File", new ActionListener() { public void actionPerformed(ActionEvent event) { int retVal = fc.showOpenDialog(frame); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fc.getSelectedFile(); herd.saveBinary(selectedFile); } }); createAndAddMenuItem(file, "Exit", KeyEvent.VK_E, "Exit application", new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } });

private JTable setUpTable(){ String[] columnNames = { "Name", "Hometown", "Rampage Behavior"}; Object[][] data = listToArray(herd.getMonsters()); JTable newTable = new JTable(data, columnNames); newTable.setPreferredScrollableViewportSize(new Dimension(500, 70)); newTable.setFillsViewportHeight(true); newTable.setAutoCreateRowSorter(true); newTable.doLayout(); return newTable; } private void updateTable() { for (Monster m : herd.getMonsters()) System.out.println(m); panel.removeAll(); table = setUpTable(); scrollPane = new JScrollPane(table); panel.add(scrollPane); panel.doLayout(); } private Object[][] listToArray(List list) { Object[][] array = new Object[list.size()][3]; for (int counter = 0; counter < list.size(); counter++) { array[counter][0] = list.get(counter).getName(); array[counter][1] = list.get(counter).getHometown(); array[counter][2] = list.get(counter).getRampageBehavior(); } return array; } public static void main(String[] args) { MonsterGUI ex = new MonsterGUI(); }

Monster Herd Persister package monsterherd; import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class MonsterHerd implements Serializable{ private List monsters; private MonsterHerdPersister persister; public MonsterHerd(){ monsters = new ArrayList (); persister = new MonsterHerdPersister(); } public List getMonsters() { return monsters; } public void saveBinary(File fileOut){ persister.saveHerdToFile(fileOut, this); }

Monster Herd Persister public void populateFromFile(File fileIn){ monsters = persister.readHerdFromFile(fileIn).getMonsters(); } public void addMonster(Monster m){monsters.add(m);} public String toString(){ StringBuilder sb = new StringBuilder(); for(Monster m: monsters) sb.append(m + "\n"); return sb.toString(); }

Monster Herd Persister package monsterherd; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class MonsterHerdPersister implements Serializable{ public void saveHerdToFile(File f, MonsterHerd mh) { ObjectOutputStream out = null; try { out = new ObjectOutputStream(new BufferedOutputStream( new FileOutputStream(f))); out.writeObject(mh); out.close(); } catch (Exception e) { e.printStackTrace(); }

Monster Herd Persister public MonsterHerd readHerdFromFile(File f) { ObjectInputStream in = null; MonsterHerd m = null; try { in = new ObjectInputStream(new BufferedInputStream( new FileInputStream(f))); m = (MonsterHerd) in.readObject(); in.close(); } catch (Exception e) { e.printStackTrace(); } return m; }

Binary I/O With List Binary I/O can be used to store and retrieve lists of objects.

Binary I/O With List package vampirebinaryio; import java.io.Serializable; public class Crypt implements Serializable { private String location; public Crypt(String location) { this.location = location; } public void setLocation(String location) { this.location = location; } public String getLocation() { return location; } public String toString(){ return "a really mysterious crypt in " + location; }

package vampirebinaryio; import java.io.Serializable; import javax.swing.JOptionPane; public class Vampire implements Serializable { private String name; private Crypt crypt; public Vampire(String name, String location) { this.name = name; crypt = new Crypt(location); } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setLocation(String location) { crypt.setLocation(location); } public String getLocation() { return crypt.getLocation(); } public void rampage() { JOptionPane.showMessageDialog(null, name + " arises from " +crypt.toString() + " and sucks people's blood " + "all night, then returns to a coffin to hide from sunlight"); } public String toString(){ return name + " inhabits " + getLocation(); }

Binary I/O With List package vampirebinaryio; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.swing.JFileChooser; import javax.swing.JOptionPane; public class VampireIODriver { private static List vampires; public VampireIODriver() { vampires = new ArrayList (); } public void prowl() { String[] options = { "Quit", "List Vampires", "Add A Vampire", "Save To File", "Load From File" }; int choice = 0;

Binary I/O With List do { choice = JOptionPane.showOptionDialog(null, "Next Action", "Next Action", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); switch (choice) { case 1: listVampires(); break; case 2: addVampire(); break; case 3: saveToFile(); break; case 4: loadFromFile(); break; } } while (choice != 0); }

public void listVampires() { StringBuilder sb = new StringBuilder(); for (Vampire v : vampires) sb.append(v.toString() + "\n"); if (sb.length() == 0) sb.append("no vampires"); JOptionPane.showMessageDialog(null, sb); } public void addVampire() { String name = JOptionPane.showInputDialog("Please enter the vampire's name"); String location = JOptionPane.showInputDialog("Please enter the vampire's location"); vampires.add(new Vampire(name, location)); } public void saveToFile() { VampirePersister persister = new VampirePersister(); JFileChooser fc = new JFileChooser(); int retVal = fc.showOpenDialog(null); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fc.getSelectedFile(); persister.saveListToFile(selectedFile, vampires); }

Binary I/O With List public void loadFromFile() { VampirePersister persister = new VampirePersister(); JFileChooser fc = new JFileChooser(); int retVal = fc.showOpenDialog(null); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fc.getSelectedFile(); vampires = persister.readListFromFile(selectedFile); } public static void main(String[] args) { VampireIODriver d = new VampireIODriver(); d.prowl(); }

Binary I/O With List package vampirebinaryio; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.List; public class VampirePersister implements Serializable{ public void saveListToFile(File f, List vl) { ObjectOutputStream out = null; try { out = new ObjectOutputStream(new BufferedOutputStream( new FileOutputStream(f))); out.writeObject(vl); out.close(); } catch (Exception e) { e.printStackTrace(); }

Binary I/O With List public List readListFromFile(File f) { ObjectInputStream in = null; List vl = null; try { in = new ObjectInputStream(new BufferedInputStream( new FileInputStream(f))); vl = (List ) in.readObject(); in.close(); } catch (Exception e) { e.printStackTrace(); } return vl; }

Download ppt "CS203 LECTURE 3 John Hurley Cal State LA. Recursion Recursion is the technique of breaking down a problem into smaller instances of the same problem."

Similar presentations