Module 2: Algorithm and Data Structures Recursion Alberto Montresor Università di Trento 2016/11/03 This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Table of contents 1 2 3 Introduction Hanoi’s Tower An example problem Introduction Goal of this lecture To understand that complex problems that may otherwise be difficult to solve, may be solved by splitting them in sub-problems To learn how to formulate programs recursively To understand and apply the three laws of recursion To implement the recursive formulation of a problem To understand how recursion is implemented by a computer system Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 1 / 21 Introduction Recursion Definition Recursion is a method of solving problems that involves reducing a problem into a smaller one, until you get a problem small enough that it can be solved trivially. Recursion often involves a function that invokes itself Several mathematical functions are defined recursively Several problems can be defined recursively Some problems may be solved more efficiently with a recursive approach Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 2 / 21 Introduction Example: Factorial numbers ( 1 n≤1 n! = n · (n − 1)! n > 1 int factorial(int n) if n ≤ 1 then return 1 else return n · factorial(n − 1) Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 3 / 21 Introduction Example: Factorial numbers n=1 n=2 n=3 n=4 n=5 Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 4 / 21 Introduction Example: Factorial numbers def factorial(n): print("Start ", n) if n < 2: print("End ", 1) return 1 else: res = n*factorial(n-1) print("End ", n) return res Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 5 / 21 Introduction The three laws of recursion All recursive algorithms must obey three important laws A recursive algorithm must have a base case A recursive algorithm must call itself, recursively A recursive algorithm must move toward the base case What happens with this code? factorial(int n) return n · factorial(n − 1) Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 6 / 21 Introduction Minimum – Recursive version 1 This function returns the minimum of the elements included between i and j. minrec(int[ ] A, int i, int j) if i = j then return A[i] else m ← b(i + j)/2c return min(minrec(A, i, m), minrec(A, m + 1, j)) This function is a wrapper for the first function: int min(int[ ] A) return minrec(A, 0, len(A) − 1) Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 7 / 21 Introduction Minimum – Recursive version 2 This function returns the minimum of the elements included in the slice A[0 : i]. minrec(int[ ] A, int i) if i = 1 then return A[0] else return min(minrec(A, i − 1), A[i − 1]) This function is a wrapper for the first function: int min(int[ ] A) return minrec(A, len(A)) Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 8 / 21 Introduction Binary search – Recursive version int binarySearch(int[ ] L, int v, int i, int j) if i > j then return −1 else int m = b(i + j)/2c if L[m] = v then return m else if L[m] < v then return binarySearch(L, v, m + 1, j) else return binarySearch(L, v, i, m − 1) Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 9 / 21 Introduction Why recursion? So far, you probably don’t see the advantages of recursion... Factorial can be defined recursively, but also "iteratively": n! = Qn i=1 i int factorial(int n) int fact = 1 for i = 2 to n do fact = fact · i return fact Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 10 / 21 Introduction Why recursion? So far, you probably don’t see the advantages of recursion... Sometime there is no real advantage in execution time for the recursive versions Both the recursive and iterative versions of mimimum() are O(n) Both the recursive and iterative versions of lookup() are O(n) Executing the recursive invocations is more costly than executing the iterations Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 11 / 21 Introduction Why recursion? So far, you probably don’t see the advantages of recursion... Recursion may even be dangerous def minrec(L): if len(L) == 1: return L[0] else: return min(L[0], minrec(L[1:]) Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 12 / 21 Introduction Divide-et-impera Three phases Divide: Break the problem in smaller and independent sub-problems Impera: Solve the sub-problems recursively Combine: "merge" the solutions of subproblems There is not a unique recipe for divide-et-impera Mergesort: "divide" trivial, "combine" complex Quicksort: "divide" complex, no "combine" A creative effort is required Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 13 / 21 Hanoi’s Tower Hanoi’s tower Mathematical game Three pins n disks with different sizes Initially, all the disks are stacked in decreasing size order (from bottom to top) on the left pin https://it.wikipedia.org/wiki/File: Tower_of_Hanoi.jpeg Goal of the game Stack all the disks on the right pin in decreasing size order (from bottom to top) Never put a larger disk on top of a smaller disk You can move one disk at each step You can use the middle pin to as support Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 14 / 21 Hanoi’s Tower Hanoi’s tower hanoi(int n, int src, int dest, int middle) if n = 1 then print src → dest else hanoi(n − 1, src, middle, dest) print src → dest hanoi(n − 1, middle, dest, src) Divide-et-impera n − 1 disks from src to middle 1 disks from src to dest n − 1 disks from middle to dest Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 15 / 21 Hanoi’s Tower Hanoi’s tower hanoi(int n, int src, int dest, int middle) if n = 1 then print src → dest else hanoi(n − 1, src, middle, dest) print src → dest hanoi(n − 1, middle, dest, src) This solution is the best one (can be proven) Number of steps: T (n) = O(2n ) Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 16 / 21 An example problem Gap Gap In a list L containing n ≥ 2 integers, a gap is an index i, 0 < i < n, such that L[i − 1] < L[i]. Prove that if n ≥ 2 and L[0] < L[n − 1], L contains at least one gap Design an algorithm that, given a list L containing n ≥ 2 integers such that L[0] < L[n − 1], finds a gap in the list. Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 17 / 21 An example problem Gap By contradiction: Suppose there is no gap in the list. Then L[0] ≥ L[1] ≥ L[2] ≥ . . . L[n − 1], which contradicts the fact that L[0] < L[n − 1]. Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 18 / 21 An example problem Gap gap(int[ ] L, int n) for i = 1 to n − 1 do if L[i − 1] < L[i] then return i return −1 def gap(L): for i in range(1,len(L)): if L[i-1] < L[i]: return i return -1 Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 19 / 21 An example problem Gap Consider two indexes i < j, such that L[i] < L[j]. Let’s consider the index m in the middle between i and j. Consider the following cases: If i = j − 1, then the gap is in j If L[m] ≤ L[i] < L[j], then there is a gap between m and j; If L[i] < L[m], then there is a gap between i and m; Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 20 / 21 An example problem Gap gap(int[ ] L, int i, int j) if j == i + 1 then return j m = b(i + j)/2c if L[m] ≤ L[i] then return gap(L, m, j) else return gap(L, i, m) Alberto Montresor (UniTN) ASD - Algorithm analysis 2016/11/03 21 / 21
© Copyright 2024 Paperzz