SOFTWARE DEVELOPMENT 1 Recursion 2018W (Institute of Pervasive Computing, JKU Linz)
PRINCIPLE OF SELF-REFERENCE Recursion: Describing something in a self-similar way. An elegant, powerful and simple way to define sequences, properties, connections, algorithms, data structures, etc. by using the defined in the definition. Examples: In the BNF language definition Method m( ) that calls itself Data structures like lists or trees Program structures like statements, expressions or blocks are nested Problem decomposition, that reduces the problem to the initial problem with smaller values Properties of graphs (e.g. reachability) Software Development 1 // 2018W // 2
PRINCIPLE OF SELF-REFERENCE Simple recursion; BNF of a number: base case: without self-reference <NUMBER> := recursive definition: contains self-reference <DIGIT> <DIGIT> <NUMBER> This recursive definition describes a sequence of one to an infinite number of digits. Mutual recursion; BNF of the image to the left <IMAGE> := <PERSON> <INNER_IMAGE> <INNER_IMAGE> := <BORDER> <IMAGE> A recursion can be formed by several expressions. It is then called an indirect recursion. This recursion does not contain a base case, which means it is an infinite recursion. Software Development 1 // 2018W // 3
RECURSIVE FUNCTIONS Factorial The factorial n! of the positive integer number n is defined as follows: n! = 1 if n = 1 n * (n-1)! otherwise This can be directly translated to the following method: static long factorialrecursive(int n) { return n == 1? 1 : n * factorialrecursive(n - 1); SWE1.11 / Factorial.java Software Development 1 // 2018W // 4
RECURSIVE FACTORIAL METHOD What happens exactly when executing factorialrecursive(4)? 24 = factorialrecursive(4) A new namespace is created for the method call. In it we have the parameter n = 4. In this call 4 * 3! is evaluated. return 4 * 6 = factorialrecursive(3) return 3 * 2 = factorialrecursive(2) return 2 * 1 = factorialrecursive(1) return 1 After several calls of the factorial method, we reach a namespace in which the parameter n = 1. In this case it just returns 1 (base case). Software Development 1 // 2018W // 5
ITERATIVE FACTORIAL METHOD Every recursive solution to a problem can also be solved iteratively! static long factorialrecursive(int n) { return n == 1? 1 : n * factorialrecursive(n - 1); Recursive: Similar behaviour is repeated by calling itself. static long factorialiterative(int n) { long result = 1; while (n > 1) result *= n--; //same as: for (int i = 2; i <= n; i++) result *= i; return result; Iterative: Only 1 method call; similar behaviour is repeated in a loop. Software Development 1 // 2018W // 6
RECURSIVE FIBONACCI METHOD Fibonacci numbers are defined as follows: fib(n) = 0 if n = 0 1 if n = 1 fib(n-2) + fib(n-1) if n > 1 static int fib(int n) { if (n < 3) return 1; else return fib(n - 1) + fib(n - 2); SWE1.11 / Fibonacci.java Software Development 1 // 2018W // 7
RECURSIVE FIBONACCI METHOD As long as n is larger than 2, fib leads to two further calls of fib. The parameter n decreases each time. Example: fib(4) 3 = fib(4) fib(4) return 2 = fib(3) + 1 = fib(2) fib(2) return 1 = fib(2) + 1 = fib(1) return 1 return 1 return 1 Software Development 1 // 2018W // 8
BINOMIAL COEFFICIENT The binomial coefficient is defined as follows: bin(n,k) = Example: bin(4,2) 1 if k = 0 1 if n = k bin(n-1,k-1) + bin(n-1,k) if k 0 k n 6 = bin(4,2) 3 = bin(3,1) + 3 = bin(3,2) 1 = bin(2,0) + 2 = bin(2,1) 1 1 = bin(1,0) Recursive solution is elegant and easy to describe, but not necessarily also efficient. + 1 = bin(1,1)... 1 1 Software Development 1 // 2018W // 9
PREVIOUS EXAMPLE: BINARY SEARCH We have a sorted array, want to find index of a number. Idea: Reduce section that needs to be searched step by step. Example: Find element (index) with value 22 a [0] [1] [2] [3] [4] [15] 2 4 5 7 8 9 12 14 17 19 22 25 27 28 33 37 left center right 2 4 5 7 8 9 12 14 17 19 22 25 27 28 33 37 left center right 2 4 5 7 8 9 12 14 17 19 22 25 27 28 33 37 left right center 2 4 5 7 8 9 12 14 17 19 22 25 27 28 33 37 left = center = right Software Development 1 // 2018W // 10
BINARY SEARCH RECURSIVE AND ITERATIVE //Search for x in array a. Return position or -1 if not found static int searchrecursive(int[] a, int x) { //Start search with complete array (left=0, right=a.length-1) return searchrecursive(a, x, 0, a.length-1); Simpler interface: less parameters static int searchrecursive(int[] a, int x, int left, int right) { if (left > right) return -1; int center = (left + right) / 2; if (x > a[center]) return searchrecursive(a, x, center + 1, right); if (x < a[center]) return searchrecursive(a, x, left, center - 1); return center; Recursive method //Search for x in array a. Return position or -1 if not found static int searchiterative(int[] a, int x) { int left = 0, center = 0, right = a.length - 1; while (left <= right) { center = (left + right) / 2; if (x > a[center]) left = center + 1; else if (x < a[center]) right = center - 1; else break; if (left > right) return -1; return center; Software Development 1 // 2018W // 11 Iterative implementation SWE1.11 / BinarySearch.java
BINARY SEARCH RECURSIVE Recursive calls, each time with a further reduced array: searchrecursive (a, 22, 0, 15) a [0] [1] [2] [3] [4] [5] [6] [7] [8] [9][10][11][12][13][14][15] 2 4 5 7 8 9 12 14 17 19 22 25 27 28 33 37 left center right searchrecursive(a, 22, 8, 15) 17 19 22 25 27 28 33 37 left center right searchrecursive(a, 22, 8, 10) 17 19 22 left right center searchrecursive(a, 22, 10, 10) 22 Software Development 1 // 2018W // 12 22 left = center = right
RECURSIVE METHODS If a method calls itself, we are talking about a recursive call, and the method is named a recursive method. Each method call has its own independent name space, with a new set of local variables and parameters. For the executing machine it is of no consequence if it is a normal or recursive call! Advantages: Recursive implementations are often much easier to describe and understand than iterative solutions (see factorial or Fibonacci) Keep in mind: Easy to describe does not imply it is also easy to process: Especially on high recursive depths the memory requirements and overhead for method calls can get significant. Iterative implementations have usually a higher performance. Software Development 1 // 2018W // 13
RECURSIVE METHODS Two things are required to define a recursive method: Recursive definition (Rekursionsschritt): A description how a larger problem is composed of smaller problems. Base case (Rekursionsanker): There has to be at least one case in which the problem can be solved without further recursive calls. Every recursive call has to reach at some point the base case, either directly or indirectly through further recursive calls. The base case can be compared to the termination condition of a loop: If it is never fulfilled, the loop never stops and we get an infinite loop. The same applies to recursions: If the base case is not reached, the recursion never stops and we get an infinite recursion. Software Development 1 // 2018W // 14
RECURSIVE DATA STRUCTURES Recursion is also a powerful tool to describe data structures, not only algorithms. Examples: A list is a single element followed by a list or the empty list A set is a single element joined with a set or an empty set A binary tree is a single element followed by a left and right sub-tree or an empty tree Software Development 1 // 2018W // 15
RECURSION IN TREES Non-recursive definition of trees: A tree T consists of nodes N and edges E, so T = (N, E). Nodes carry information and they are linked by edges, so E = N x N. Edges of a tree are directed, so they are pairs of nodes (n1, n2). For each edge e = (n1, n2), n1 is called the parent node and n2 the child node. There are no cyclical connections between node sets. A binary tree is a tree, in which each node has at most 2 child nodes: n N : { n i N (n,n i ) E 2 Software Development 1 // 2018W // 16
RECURSION IN BINARY TREES Recursive definition of binary trees: A binary tree T is either empty, or it has the following shape: r r is a node. T 1 T 2 T 1 and T 2 are binary trees. A binary tree may only have a finite number of nodes (termination condition!). The node r is called the root of tree T. T 1 and T 2 are called left and right sub tree of the root r. If a node has neither a left or right child node (T 1 and T 2 are empty), it is called a leaf. A node that is neither the root nor is it a leaf (it has at least one child node), is called an inner node. Software Development 1 // 2018W // 17
RECURSION IN BINARY TREES root inner nodes leafs Software Development 1 // 2018W // 18
BINARY TREES IN JAVA root:tree Tree left content right left: Tree content: Object right: Tree left :Tree content right :String "Linz" left :Tree content right Tree(content: Object) Tree(left: Tree, content: Tree, right: Tree)... :String "Steyr" :String "Enns" Software Development 1 // 2018W // 19
BINARY TREES IN JAVA public class Tree { Object content; Tree left, right; // create leaf node public Tree(Object content) { this.content = content; this.left = null; this.right = null; // node content / value // left and right sub-trees A node has 3 properties: 1 object reference (content) 2 tree references Constructors // create inner node public Tree(Tree left, Object content, Tree right) { this.content = content; this.left = left; this.right = right; // convert this node to its String representation Customize the tostring method. public String tostring() { if (left == null && right == null) return "leaf " + content; return "tree " + content; SWE1.11 / Tree.java Software Development 1 // 2018W // 20
TRAVERSAL OF BINARY TREES Preorder Traversal Visit root Visit left sub tree in preorder Visit right sub tree in preorder 2 3 4 1 5 6 7 Inorder Traversal Visit left sub tree in inorder Visit root Visit right sub tree in inorder 2 1 3 4 6 5 7 Postorder Traversal Visit left sub tree in postorder Visit right sub tree in postorder Visit root 3 1 2 7 6 4 5 Software Development 1 // 2018W // 21
TRAVERSAL OF BINARY TREES public class Tree { Object content; Tree left, right; //... // node content / value // left and right sub-trees public void preorder() { System.out.println(this); if (left!= null) left.preorder(); if (right!= null) right.preorder(); public void inorder() { if (left!= null) left.inorder(); System.out.println(this); if (right!= null) right.inorder(); public void postorder() { if (left!= null) left.postorder(); if (right!= null) right.postorder(); System.out.println(this); // print node // traverse left tree // traverse right tree // traverse left tree // print node // traverse right tree // traverse left tree // traverse right tree // print node SWE1.11 / Tree.java Software Development 1 // 2018W // 22
EXAMPLE :: INORDER TRAVERSAL Printing an arithmetic expression: Store operators in inner nodes and operands in leaves Whenever an operation requires 2 operands, enclose it in parentheses: - / + Unary minus: No parentheses x + x - + - - 2 3-6 3 1 3 9 5 7 4 ((((3+1)x-3)/((9-5)+2))-((3x(7-4))+-6)) Software Development 1 // 2018W // 23
EXAMPLE :: INORDER TRAVERSAL public class Tree { Object content; Tree left, right; //... // node content / value // left and right sub-trees public void bracketsinorder() { if (left!= null) { if (right!= null) System.out.print("("); left.bracketsinorder(); System.out.print(content); If there is a left sub tree, traverse it first Whenever there are two sub trees, output parentheses. if (right!= null) { right.bracketsinorder(); if (left!= null) System.out.print(")"); Print node contents Traverse right sub tree SWE1.11 / Tree.java Software Development 1 // 2018W // 24
EXAMPLE :: INORDER TRAVERSAL public class Tree { Object content; Tree left, right; //... // node content / value // left and right sub-trees / Create leaves Create inner nodes public static void main(string[] args) { Tree a = new Tree(new Character('A')); Tree b = new Tree(new Character('B')); Tree f = new Tree(new Character('F')); Tree x = new Tree(new Character('X')); Tree y = new Tree(new Character('Y')); Tree mult = new Tree(a, new Character('*'), b); Tree neg = new Tree(null, new Character('-'), f); Tree plus = new Tree(neg, new Character('+'), mult); Tree minus = new Tree(x, new Character('-'), y); Tree div = new Tree(plus, new Character('/'), minus); div.bracketsinorder(); System.out.println(); Output: ((-F+(A*B))/(X-Y)) - F + - x A B X Y SWE1.11 / Tree.java Software Development 1 // 2018W // 25
CLASSIFICATION OF RECURSIVE METHODS Mutual (indirect) recursion Method m calls another method n, that in turn calls m. Tail recursion Recursive call is the last statement in the method. f(x) = g(x) if P(x) f(r(x)) if not P(x) P(x) is a boolean function (termination condition) r(x) is any arbitrary code, that does NOT call f(x) Software Development 1 // 2018W // 26
EXAMPLES :: TAIL RECURSION // Compute 2 to the power of n. static int twotopowerof(int n) { if (n == 0) return 1; return 2 * twotopowerof(n - 1); Recursive call is always last expression in the methods. // Compute greatest common divisor (gcd) of x and y. static int gcd(int x, int y) { if (y == 0) return x; return gcd(y, x % y); Compare to iterative solution: while (y!= 0) { z = x % y; x = y; y = z; Software Development 1 // 2018W // 27
CLASSIFICATION OF RECURSIVE METHODS Linear recursion A recursive method is called linear, if it calls itself at most once in one run. f(x) = g(x) if P(x) h(x, f(r(x))) otherwise Plain linear recursion A plain linear recursive method only passes modified parameters to the next recursive call; e.g. static int f(int x, int y) { if (y < 0) return 0; else if (y == 0) return x; else return f(x * y, y - 1); Software Development 1 // 2018W // 28
EXAMPLES :: LINEAR RECURSION static int factorial(int n) { if (n == 0) return 1; else return n * factorial(n - 1); Linear recursion: single recursive call static int fib(int n) { if (n <= 1) return 1; else return fib(n - 1) + fib(n - 2); Not a linear recursion! 2 (or more) recursive calls Software Development 1 // 2018W // 29
(FIBONACCI-) RECURSION IN NATURE Echinacea purpurea 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 Software Development 1 // 2018W // 31
(FIBONACCI-) RECURSION IN NATURE 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 Software Development 1 // 2018W // 32
(FIBONACCI-) RECURSION IN NATURE 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 Software Development 1 // 2018W // 33
(FIBONACCI-) RECURSION IN NATURE 3 Rotations 5 Leaves 5 Rotations 8 Leaves Software Development 1 // 2018W // 34
(FIBONACCI-) RECURSION IN NATURE 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 Software Development 1 // 2018W // 35
(FIBONACCI-) RECURSION IN NATURE 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 Software Development 1 // 2018W // 36
FAMOUS RECURSIVE ALGORITHMS Divide and Conquer: Principle to split a problem in smaller problems with identical structure but lower complexity. Quicksort: Simple and mostly fast sorting algorithm. Mergesort: Simple sorting algorithm. Towers of Hanoi: Transfer of a stack of disks to another place, using only a single storage place. Eight queens puzzle: Arrange 8 queens on a chess board so that no two queens can attack each other. Backtracking: Systematical testing of an approach, with backtracking and subsequent choice of alternatives. Several operations on data structures, e.g. on trees: Traversal, searching, insertions, Software Development 1 // 2018W // 37
TOWER OF HANOI Original problem definition (by Eduard Lucas 1883) A monastic order in Benares owns the tower of the Brahma, which consists of 3 rods, on which 64 golden discs are distributed. All discs have a different size and weight. In the beginning all discs are placed on the left rod. The monks are tasked with moving all discs to the middle rod, but they can only move a single disc at a time. Because of the weight of the discs, they always have to be placed on larger discs. When all discs have been moved to the middle rod, the monks task is completed and the world will end. (e.g. http://www.mazeworks.com/hanoi/index.htm) Software Development 1 // 2018W // 38
TOWER OF HANOI When can we move the lowest disc of a stack? All other n-1 discs have to removed from the rod. Where do we move the lowest disc? Directly to the destination rod. What is the solution for a single disc? Move disc 1 from A to B. What is the solution for 2 discs? Move disc 1 from A to C. Move disc 2 from A to B. Move disc 1 from C to B. Generalization? Software Development 1 // 2018W // 39
TOWER OF HANOI IN JAVA public class Hanoi { static void hanoi(int height, char source, char stack, char destination) { if (height == 1) { System.out.println("move disk 1 from " + source + " to " + destination); else { hanoi(height - 1, source, destination, stack); System.out.println("move disk " + height + " from " + source + " to " + destination); hanoi(height - 1, stack, source, destination); public static void main(string[] args) { hanoi(3, 'A', 'C', 'B'); move disk 1 from C to A SWE1.11 / Hanoi.java Move 3 discs from A to B, C is stack. Software Development 1 // 2018W // 40 Output: move disk 1 from A to B move disk 2 from A to C move disk 1 from B to C move disk 3 from A to B move disk 2 from C to B move disk 1 from A to B
TOWER OF HANOI RUNTIME Original problem with 64 discs; If we assume 1 step takes 1 ns = 1E-9 s Steps: 2^64 1 = 18.446.744.073.709.551.615 = 1,8E19 Total time: 1,8E19 * 1E-9 / (60*60*24*365) = 584 years Software Development 1 // 2018W // 41
COMMON MISTAKES WITH RECURSIONS Base case is missing or incorrect. Termination condition is never met or checked. Wrong direction, e.g. counting up, but termination condition is at 0. Incorrect recursion step. Software Development 1 // 2018W // 42
SOLUTION TO THE TOWER OF HANOI To move disc i from rod A to rod B Disc i has to be the uppermost disc on rod A. There must not be a smaller disc than i on rod B. Therefore all smaller discs 1, 2,, i-1 need to be on the 3rd rod C. How do we get all the smaller discs to C? Apply the algorithm recursively! To move n discs from the source rod to the destination rod; 3rd rod is stack: Move the n-1 upper discs from the source rod to the stack rod. Move disc n from the source rod to the destination rod. Move n-1 discs from the stack rod to the destination rod. Software Development 1 // 2018W // 43
SOFTWARE DEVELOPMENT 1 Recursion 2018W (Institute of Pervasive Computing, JKU Linz)