Presentation is loading. Please wait.

Presentation is loading. Please wait.

Department of Computer Science,

Similar presentations


Presentation on theme: "Department of Computer Science,"— Presentation transcript:

1 Department of Computer Science,
11 CHAPTER RECURSION Copyright © 2013 by John Wiley & Sons. All rights reserved. Slides by James Tam Department of Computer Science, University of Calgary May 27, 2013

2 Chapter Goals To learn to “think recursively”
To be able to use recursive helper functions To understand the relationship between recursion and iteration To understand when the use of recursion affects the efficiency of an algorithm To analyze problems that are much easier to solve by recursion than by iteration To process data with recursive structures using mutual recursion Copyright © 2013 by John Wiley & Sons. All rights reserved.

3 Contents Triangle Numbers Revisited
Problem Solving: Thinking Recursively Recursive Helper Functions The Efficiency of Recursion Permutations Backtracking Mutual Recursion Copyright © 2013 by John Wiley & Sons. All rights reserved.

4 11.1 Triangle Numbers Revisited
Triangle shape of side length 4: [] [][] [][][] [][][][] Will use recursion to compute the area of a triangle of width n , assuming each [] square has an area of 1 Also called the nth triangle number The third triangle number is 6, the fourth is 10 Copyright © 2013 by John Wiley & Sons. All rights reserved.

5 Handling Triangle of Width 1
The triangle consists of a single square Its area is 1 Take care of this case first: def triangleArea(sideLength) : if sideLength == 1 : return 1 . . . Copyright © 2013 by John Wiley & Sons. All rights reserved.

6 Handling The General Case
Assume we know the area of the smaller, colored triangle: [] [][] [][][] [][][][] Area of larger triangle can be calculated as To get the area of the smaller triangle Call the triangleArea() function: area = smallerArea + sideLength smallerSideLength = sideLength - 1 smallerArea = triangleArea(smallerSideLength) Copyright © 2013 by John Wiley & Sons. All rights reserved.

7 Computing the Area of a Triangle With Width 4
triangleArea() function makes a smaller triangle of width 3 It calls triangleArea() on that triangle That function makes a smaller triangle of width 2 That function makes a smaller triangle of width 1 That function returns 1 The function returns smallerArea + sideLength = = 3 The function returns smallerArea + sideLength = = 6 The function returns smallerArea + sideLength = = 10 Copyright © 2013 by John Wiley & Sons. All rights reserved.

8 Recursive Computation
A recursive computation solves a problem by using the solution to the same problem with simpler inputs Call pattern of a recursive function is complicated Key: Don’t think about it Copyright © 2013 by John Wiley & Sons. All rights reserved.

9 Successful Recursion Every recursive call must simplify the computation in some way There must be special cases to handle the simplest computations directly Copyright © 2013 by John Wiley & Sons. All rights reserved.

10 Other Ways to Compute Triangle Numbers
The area of a triangle equals the sum: sideLength Using a simple loop: area = 0; for i in range (1, (sideLength+1), 1) : area = area + i Using math: n = n × (n + 1)/2 => n * (n + 1) / 2 Copyright © 2013 by John Wiley & Sons. All rights reserved.

11 Trianglenumbers.py Copyright © 2013 by John Wiley & Sons. All rights reserved.

12 Special Topic 11.1 An Object-Oriented version of the triangles program. General case: compute the area of the larger triangle as smallerArea + self._sideLength. To get the smaller area: class Triangle def _ _init_ _ (self, sideLength) : self._sideLength = sideLength def getArea(self) : if self._sideLength == 1 : return 1 . . . smallerTriangle = Triangle(self._sideLength - 1) smallerArea = smallerTriangle.getArea() area = smallerArea + self._sideLength Copyright © 2011 by John Wiley & Sons. All rights reserved.

13 Common Error 11.1 Infinite recursion: Causes:
A function calling itself over and over with no end in sight. The computer needs some amount of memory for bookkeeping during each call. After some number of calls, all memory that is available for this purpose is exhausted. Your program shuts down and reports a “stack overflow”. Causes: The arguments don’t get simpler or because a special terminating case is missing. Copyright © 2011 by John Wiley & Sons. All rights reserved.

14 11.2 Problem Solving: Thinking Recursively
Problem: Test whether a sentence is a palindrome Palindrome: A string that is equal to itself when you reverse all characters A man, a plan, a canal – Panama! Go hang a salami, I’m a lasagna hog Madam, I’m Adam Copyright © 2013 by John Wiley & Sons. All rights reserved.

15 Implement IsPalindrome() Function
## Tests whether a string is a palindrome. text a string that is being checked True if text is a palindrome, False otherwise # def isPalindrome(text) : . . . Copyright © 2013 by John Wiley & Sons. All rights reserved.

16 Thinking Recursively: Step 1
Consider various ways to simplify inputs. Several possibilities: Remove the first character Remove the last character Remove both the first and last characters Remove a character from the middle Cut the string into two halves Copyright © 2013 by John Wiley & Sons. All rights reserved.

17 Thinking Recursively: Step 2 (1)
Combine solutions with simpler inputs into a solution of the original problem. Most promising simplification: Remove both first and last characters. “adam, I’m Ada” is a palindrome too! Thus, a word is a palindrome if The first and last letters match, and Word obtained by removing the first and last letters is a palindrome Copyright © 2013 by John Wiley & Sons. All rights reserved.

18 Thinking Recursively: Step 2 (2)
What if first or last character is not a letter? Ignore it If the first and last characters are letters, check whether they match; if so, remove both and test shorter string If last character isn’t a letter, remove it and test shorter string If first character isn’t a letter, remove it and test shorter string Copyright © 2013 by John Wiley & Sons. All rights reserved.

19 Thinking Recursively: Step 3
Find solutions to the simplest inputs. Strings with two characters No special case required; step two still applies Strings with a single character They are palindromes The empty string It is a palindrome Copyright © 2013 by John Wiley & Sons. All rights reserved.

20 Thinking Recursively: Step 4 (1)
Implement the solution by combining the simple cases and the reduction step. def isPalindrome(text) : length = len(text) # Separate case for shortest strings. if length <= 1 : return True else : # Get first and last characters, converted to # lowercase. first = text[0].lower() last = text[length - 1].lower() Continued Copyright © 2013 by John Wiley & Sons. All rights reserved.

21 Thinking Recursively: Step 4 (2)
# Non base case if first.isalpha() and last.isalpha() : # Both are letters. if first == last : # Remove both first and last character. shorter = text[1 : length - 1] return isPalindrome(shorter) else : return False elif not last.isalpha() : # Remove last character. shorter = text[0 : length - 1] # Remove first character. shorter = text[1 : length] Copyright © 2013 by John Wiley & Sons. All rights reserved.

22 11.3 Recursive Helper functions
Sometimes it is easier to find a recursive solution if you make a slight change to the original problem. Consider the palindrome test of previous section. It is a bit inefficient to construct new string objects in every step. Copyright © 2013 by John Wiley & Sons. All rights reserved.

23 Substring Palindromes (1)
Rather than testing whether the sentence is a palindrome, check whether a substring is a palindrome: ## Recursively tests whether a substring is # a palindrome. text a string that is being checked start the index of the first character of the substring end the index of the last character of the substring True if the substring is a palindrome # def substringIsPalindrome(text, start, end) : Copyright © 2013 by John Wiley & Sons. All rights reserved.

24 Substring Palindromes (2)
Then, simply call the helper function with positions that test the entire string: def isPalindrome(text) : return substringIsPalindrome(text, 0, len(text) – 1) Copyright © 2013 by John Wiley & Sons. All rights reserved.

25 Recursive Helper function SubStringIsPalindrome(): 1
def substringIsPalindrome(text, start, end) : # Separate case for substrings of length 0 and 1. if start >= end : return True else : # Get first and last characters, converted to lowercase. first = text[start].lower() last = text[end].lower() Continued Copyright © 2013 by John Wiley & Sons. All rights reserved.

26 Recursive Helper function SubStringIsPalindrome(): 2
if first.isalpha() and last.isalpha() : if first == last : # Test substring that doesn’t contain the matching # letters. return substringIsPalindrome (text, start + 1, end - 1) else : return False elif not last.isalpha() : # Test substring that doesn’t contain the last character. return substringIsPalindrome(text, start, end - 1) # Test substring that doesn’t contain the first # character. return substringIsPalindrome(text, start + 1, end) Copyright © 2013 by John Wiley & Sons. All rights reserved.

27 11.4 The Efficiency of Recursion
Fibonacci sequence: Sequence of numbers defined by f1 = 1 f2 = 1 fn = fn-1 + fn-2 First ten terms: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 Copyright © 2013 by John Wiley & Sons. All rights reserved.

28 Recursivefib.py Copyright © 2013 by John Wiley & Sons. All rights reserved.

29 Efficiency of Recursion
Recursive implementation of fib() is straightforward. Watch the output closely as you run the test program. First few calls to fib() are quite fast. For larger values, the program pauses an amazingly long time between outputs. To find out the problem, let’s insert trace messages. Copyright © 2013 by John Wiley & Sons. All rights reserved.

30 Recursivefibtracer.py: 1
Copyright © 2013 by John Wiley & Sons. All rights reserved.

31 Recursivefibtracer.py: 2
Copyright © 2013 by John Wiley & Sons. All rights reserved.

32 Call Pattern of Recursive Fib() Function
Copyright © 2013 by John Wiley & Sons. All rights reserved.

33 Efficiency of Recursion
The function takes so long because it computes the same values over and over. Computation of fib(6) calls fib(3) three times. Imitate the pencil-and-paper process to avoid computing the values more than once. Copyright © 2013 by John Wiley & Sons. All rights reserved.

34 Efficiency of Recursion
Occasionally, a recursive solution runs much slower than its iterative counterpart. In most cases, the recursive solution is only slightly slower. The iterative isPalindrome() performs only slightly better than recursive solution. Each recursive function call takes a certain amount of processor time Copyright © 2013 by John Wiley & Sons. All rights reserved.

35 Loopfib.py (1) Copyright © 2013 by John Wiley & Sons. All rights reserved.

36 Loopfib.py (2) Copyright © 2013 by John Wiley & Sons. All rights reserved.

37 Efficiency of Recursion
Smart compilers can avoid recursive function calls if they follow simple patterns. Most compilers don’t do that In many cases, a recursive solution is easier to understand and implement correctly than an iterative solution . “To iterate is human, to recurse divine.” - L. Peter Deutsch Copyright © 2011 by John Wiley & Sons. All rights reserved.

38 Iterative IsPalindrome() Function
def isPalindrome(text) : start = 0 end = len(text) - 1 while start < end : first = text[start].lower() last = text[end].lower() if first.isalpha() and last.isalpha() : # Both are letters. if first == last : start = start + 1 end = end - 1 else : return False if not last.isalpha() Copyright © 2013 by John Wiley & Sons. All rights reserved.

39 11.5 Permutations Design a class that will list all permutations of string, where a permutation is a rearrangement of the letters The string "eat" has six permutations: "eat" "eta" "aet" "ate" "tea" "tae" Copyright © 2013 by John Wiley & Sons. All rights reserved.

40 Generate All Permutations (1)
Generate all permutations that start with 'e', then 'a', then 't' The string "eat" has six permutations: "eat" "eta" "aet" "ate" "tea" "tae" Copyright © 2013 by John Wiley & Sons. All rights reserved.

41 Generate All Permutations (2)
Generate all permutations that start with 'e', then 'a', then 't' To generate permutations starting with 'e', we need to find all permutations of "at" This is the same problem with simpler inputs Use recursion Copyright © 2013 by John Wiley & Sons. All rights reserved.

42 Implementing Permutations() Function
Loop through all positions in the word to be permuted For each of them, compute the shorter word obtained by removing the ith letter: Compute the permutations of the shorter word: shorter = word[ : i] + word[i + 1 : ] shorterPermutations = permutations(shorter) Copyright © 2013 by John Wiley & Sons. All rights reserved.

43 Implementing Permutations() Function
Add the removed letter to the front of all permutations of the shorter word: Special case for the simplest string, the empty string, which has a single permutation - itself for s in shorterPermutations : result.append(word[i] + s) Copyright © 2011 by John Wiley & Sons. All rights reserved.

44 Permutations.py (1) Copyright © 2013 by John Wiley & Sons. All rights reserved.

45 Permutations.py (2) Copyright © 2013 by John Wiley & Sons. All rights reserved.

46 11.6 Backtracking Backtracking examines partial solutions, abandoning unsuitable ones and returning to consider other candidates. Can be used to solve crossword puzzles. escape from mazes. find solutions to systems that are constrained by rules. Copyright © 2013 by John Wiley & Sons. All rights reserved.

47 Backtracking Characteristic Properties
A procedure to examine a partial solution and determine whether to: accept it as an actual solution or, abandon it (because it either violates some rules or can never lead to a valid solution). A procedure to extend a partial solution, generating one or more solutions that come closer to the goal. Copyright © 2013 by John Wiley & Sons. All rights reserved.

48 Recursive Backtracking Algorithm
Solve(partialSolution) Examine(partialSolution). If accepted Add partialSolution to the list of solutions. Else if not abandoned For each p in extend(partialSolution) Solve(p). Copyright © by John Wiley & Sons. All rights reserved.

49 Eight Queens Problem (1)
Problem: position eight queens on a chess board so that none of them attacks another according to the rules of chess A solution: Copyright © 2013 by John Wiley & Sons. All rights reserved.

50 Eight Queens Problem (2)
Easy to examine a partial solution: If two queens attack one another, reject it Otherwise, if it has eight queens, accept it Otherwise, continue Easy to extend a partial solution: Add another queen on an empty square Systematic extensions: Place first queen on row 1 Place the next on row 2 Etc. Copyright © 2013 by John Wiley & Sons. All rights reserved.

51 Function: Examine() def examine(partialSolution) :
for i in range(0, len(partialSolution)) : for j in range(i + 1, len(partialSolution)) : if attacks(partialSolution[i], partialSolution[j]) : return ABANDON if len(partialSolution) == NQUEENS : return ACCEPT else : return CONTINUE Copyright © 2013 by John Wiley & Sons. All rights reserved.

52 Function: Extend() def extend(partialSolution) : results = []
row = len(partialSolution) + 1 for column in "abcdefgh" : newSolution = list(partialSolution) newSolution.append(column + str(row)) results.append(newSolution) return results Copyright © 2013 by John Wiley & Sons. All rights reserved.

53 Diagonal Attack To determine whether two queens attack each other diagonally: Check whether slope is ±1 (row2 – row1)/(column2 – column1) = ±1 row2 – row1 = ±(column2 – column1) |row2 – row1| = |column2 – column1| Copyright © 2013 by John Wiley & Sons. All rights reserved.

54 Backtracking in the Four Queens Problem (1)
Copyright © 2013 by John Wiley & Sons. All rights reserved.

55 Backtracking in the Four Queens Problem (2)
Starting with a blank board, four partial solutions with a queen in row 1 When the queen is in column 1, four partial solutions with a queen in row 2 Two are abandoned immediately Other two lead to partial solutions with three queens and , all but one of which are abandoned One partial solution is extended to four queens, but all of those are abandoned as well Copyright © 2013 by John Wiley & Sons. All rights reserved.

56 Queens.py (1) Copyright © 2013 by John Wiley & Sons. All rights reserved.

57 Queens.py (2) Copyright © 2013 by John Wiley & Sons. All rights reserved.

58 Queens.py (3) Copyright © 2013 by John Wiley & Sons. All rights reserved.

59 11.7 Mutual Recursion Problem: Compute the value of arithmetic expressions such as: 3 + 4 * 5 (3 + 4) * (2 - (3 - (4 - 5))) Computing the expression is complicated * and / bind more strongly than + and – Parentheses can be used to group sub-expressions Copyright © 2013 by John Wiley & Sons. All rights reserved.

60 Syntax Diagrams for Evaluating an Expression
Copyright © 2013 by John Wiley & Sons. All rights reserved.

61 Mutual Recursion An expression can be broken down into a sequence of terms, separated by + or – Each term is broken down into a sequence of factors, separated by * or / Each factor is either a parenthesized expression or a number The syntax trees represent which operations should be carried out first Copyright © 2013 by John Wiley & Sons. All rights reserved.

62 Syntax Trees for Two Expressions
Copyright © 2013 by John Wiley & Sons. All rights reserved.

63 Mutual Recursion In a mutual recursion, a set of cooperating functions calls each other repeatedly To compute the value of an expression, implement 3 functions that call each other recursively: expression() term() factor() Copyright © 2013 by John Wiley & Sons. All rights reserved.

64 Function: Expression()
def expression(tokens) : value = term(tokens) done = False while not done and len(tokens) > 0 : next = tokens[0] if next == "+" or next == "-" : tokens.pop(0) # Discard "+" or "-" value2 = term(tokens) if next == "+" : value = value + value2 else : value = value - value2 done = True return value Copyright © 2013 by John Wiley & Sons. All rights reserved.

65 Function: Term() The term() function calls factor() in the same way, multiplying or dividing the factor values def term(tokens) : value = factor(tokens) done = False while not done and len(tokens) > 0: next = tokens[0] if next == "*" or next == "/" : tokens.pop(0) value2 = factor(tokens) if next == "*" : value = value * value2 else : value = value / value2 done = True return value Copyright © 2013 by John Wiley & Sons. All rights reserved.

66 Function: Factor() def factor(tokens) : next = tokens.pop(0)
if next == "(" : value = expression(tokens) tokens.pop(0) # Discard ")" else : value = next return value Copyright © 2013 by John Wiley & Sons. All rights reserved.

67 Trace (3 + 4) * 5 To see the mutual recursion clearly, trace through the expression (3+4)*5: expression() calls term() term() calls factor() factor() consumes the ( input factor() calls expression() expression() returns eventually with the value of 7, having consumed This is the recursive call. factor() consumes the ) input factor() returns 7 term() consumes the inputs * and 5 and returns 35 expression() returns 35 Copyright © 2013 by John Wiley & Sons. All rights reserved.

68 Evaluator.py (1) Copyright © 2013 by John Wiley & Sons. All rights reserved.

69 Evaluator.py (2) Copyright © 2013 by John Wiley & Sons. All rights reserved.

70 Evaluator.py (3) Copyright © 2013 by John Wiley & Sons. All rights reserved.

71 Evaluator.py (4) Copyright © 2011 by John Wiley & Sons. All rights reserved.

72 Summary Understand the control flow in a recursive computation.
A recursive computation solves a problem by using the solution to the same problem with simpler inputs. For a recursion to terminate, there must be special cases for the simplest values. Design a recursive solution to a problem. Copyright © 2013 by John Wiley & Sons. All rights reserved.

73 Summary Identify recursive helper functions for solving a problem.
Sometimes it is easier to find a recursive solution if you make a slight change to the original problem. Contrast the efficiency of recursive and non-recursive algorithms. Occasionally, a recursive solution runs much slower than its iterative counterpart. However, in most cases, the recursive solution is only slightly slower. In many cases, a recursive solution is easier to understand and implement correctly than an iterative solution. Copyright © 2011 by John Wiley & Sons. All rights reserved.

74 Summary Review a complex recursion example that cannot be solved with a simple loop. The permutations of a string can be obtained more naturally through recursion than with a loop. Use backtracking to solve problems that require trying out multiple paths. Backtracking examines partial solutions, abandoning unsuitable ones and returning to consider other candidates. Copyright © 2011 by John Wiley & Sons. All rights reserved.

75 Summary Recognize the phenomenon of mutual recursion in an expression evaluator. In a mutual recursion, cooperating functions or methods call each other repeatedly. Copyright © 2011 by John Wiley & Sons. All rights reserved.


Download ppt "Department of Computer Science,"

Similar presentations


Ads by Google