1 / 58

Chapter 5

Chapter 5. Recursion as a problem solving technique. Recalling recursion. This chapter considers organized ways to make successive guesses at a solution If a particular guess is a dead-end solution, you backtrack to the beginning and then make another guess

ludwig
Download Presentation

Chapter 5

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 5 Recursion as a problem solving technique

  2. Recalling recursion • This chapter considers organized ways to make successive guesses at a solution • If a particular guess is a dead-end solution, you backtrack to the beginning and then make another guess • Recursion and backtracking can be used to solve…

  3. The 8 Queens problem • A chessboard contains 64 squares • There are 8 horizontal rows • There are 8 vertical columns • The most powerful piece in the game of chess is the queen because she can move with almost impunity • She can go horizontal or vertical as many spaces as she likes • She can go diagonally in any direction as many spaces as she likes

  4. More on the 8 Queens problem Our task is to place 8 queens on the chessboard in such a way that no queen can attack or overtake another queen on one move

  5. Solving the problem • One strategy would be to guess, i.e. try a solution at random • There are 4,426,165,368 possible ways to arrange 8 pieces on a chessboard of 64 squares • Trying this approach would takes days for a computer to solve the problem • We’re smart because we’ve taken 2315, so we know that there must be a better way to solve the problem at hand

  6. A few observations • Since a queen can move along any horizontal or vertical path, a valid solution to the 8 queens problem has only one queen per column and per row • In other words, each row and column of the chessboard can contain only one queen if it is to be a valid solution to our problem

  7. A prototype solution • By enforcing the row/column rule, we have eliminated the possibility of a queen being on the same row or column • The reduces our number of possibly solutions from > 4 billion to 8!=40,320 solutions • A trial and error approach to 40k solutions is quite a bit more feasible than that of 4 billion • We can now provide a bit more structure for guessing the remaining placements

  8. A first pass solution • Let’s say that we place the first queen on the upper left hand corner of the chessboard (the (1,1) position) • We can now place our second queen • We know she cannot go in either the first row or the first column • We know that she cannot go on the second row of the second column because she will be vulnerable to a diagonal attack from Queen 1 • We therefore choose to place her in the second column on the third row

  9. Placing successive Queens • For Queen 3, we take an approach similar to that of Queen 2 and place her in the third column, fifth row • For Queen 4, we decide to place her in column 4, row 2 • For Queen 5, we decide to place her in column 5, row 4

  10. So far, so good, but… • We’re stuck! • We cannot place a queen in column 6 because the way that we have laid out the board with the previous 5 queens means that a Queen can attack any piece placed in column 6. • Since we have to place a Queen in column 6, we know we do not have a valid solution • We now backtrack to see if we can salvage our solution

  11. Repositioning Queen 5 • We now choose to put Queen 5 in column 5, row 8 • We still have the same problem of a completely vulnerable column 6 • This is still not an acceptable solution • There are no more alternatives to placing a Queen in column 5 with Queens 1-4 being in their current positions, so we backtrack again to the placement of Queen 4

  12. Moving Queen 4 • We now opt to move Queen 4 to row 7 and try our hand at Queens 5-8 again • Queen 5 can be placed in row 2 • Queen 6 can be placed in row 4 • …and so forth • We find that this solution will not work either and backtrack to reposition Queen 5

  13. What we have tried so far

  14. Moving Queen 5 • We move Queen 5 to row 8 • We place Queen 6 in row 2 • We place Queen 7 in row 4 • We place Queen 8 in row 6 • We now have a valid solution!

  15. Recursively solving this problem • If you can successfully place a Queen in the current column, do so • If you cannot, backtrack one column and reposition that Queen • If you have tried all those positions, backtrack one more column • Repeat until you find a column with a Queen’s position that you haven’t tried

  16. An implementation of the 8 Queens solution • This can be found in your textbook on pp. 240-242. • Typically you would want to place the first Queen randomly in the first column to discover other potential solutions • You could exhaustively search all 40k possibilities to find how many yield valid solutions to the 8 Queens problem (roughly 1k solutions)

  17. Defining languages • English and C++ are two languages with which you should already be familiar • A language is defined as a set of strings and/or symbols that communicate an idea • All programs are strings of some sort, but not all strings are programs

  18. Extending the definition of language • We can expand the definition of the word “language” to include non-programming and non-communicative variants; namely, the set of algebraic expressions forms a language. AlgebraicExpressions=[w:w is an algebraic expression] • The language AlgebraicExpressions is a set of strings that meet certain criteria (rules of syntax), but this definition does not parlay those rules

  19. Grammar (not Kelsey) • The rules for forming a string that conforms to the language are referred to as a grammar. • The grammars that will be covered in this chapter are recursive in their nature, but it should not be inferred that all grammars are recursive!

  20. Benefits to a recursive grammar • Oftentimes a recursive grammar provides an elegant solution to determining if a string is part of a language • Such a recursive algorithm is commonly called a recognition algorithm for the language

  21. Basics of Grammar • A grammar uses several special symbols • x|y means x or y • xy means x immediately followed by y • <word> means any instance of word that the language definition defines • This is probably all very foreign and new to you, so let us begin with an example to elucidate our constructs

  22. C++ Identifiers • Let’s say that we want to construct a grammar for C++ Identifiers • In other words, we want to define which identifiers are valid in the C++ language • More importantly, we want to be able to distinguish them from one another • A grammar for C++Identifiers = [w:w is a legal C++ Identifier] is simple

  23. Valid C++ Identifiers • A valid identifier must begin with a letter and is then followed by 0 or more letters and digits • The underscore character (_) is treated as a letter in this grammar • We can represent this with a syntax diagram like such:

  24. Syntax diagram vs. Grammar • A syntax diagram is a handy visual tool for people to use, but a grammar is generally a better starting point for writing a function that will test the validity of an identifier • The definition is: An identifier is a letter or an identifier followed by a letter or an identifier followed by a digit. • The grammar looks like this: • <identifier>=<letter>|<identifier><letter>|<identifier><digit> • <letter>=a|b|c|…|y|z|A|B|C|…|Y|Z|_ • <digit>=0|1|2|3|4|5|6|7|8|9

  25. A recursive definition of our grammar • The more astute of you will have noticed that – contrary to what your English teacher taught you – the word identifier appears in the definition of the word identifier • This should implicitly tell us that the definition lends itself to recursion (as do a great many (though not all) grammars)

  26. More on the recursive definition • Given a string w you can determine if it is in the language C++Identifiers by using the grammar to construct a recognition algorithm • The algorithm proceeds thusly: • If w.length=1, it is in the language if the character is a letter (this is a base case) • If w.length>1, it is in the language if the last character is a letter or digit and the first length-1 characters form an identifier

  27. Another simple language • Let us move on to a slightly more interesting example; namely, palindromes • A palindrome, as you may know, is a string that reads the same both forward and backward • Radar is a palindrome • Race car is also a palindrome

  28. Defining the language of palindromes • The language of a palindrome can be simply defined in our language as Palindromes=[w:w reads the same from right-to-left as it does left-to-right] • Your visceral reaction might be to say that if w-1 is a palindrome, then w is a palindrome • This is the case with “deeds” • However, this is not the case with “radar”

  29. A little thinking… • If you consider the problem holistically, you will probably notice that you must consider the letters in pairs. • In this manner, w is a palindrome if w-first-last is a palindrome, i.e. if we strip the first and last letters of w • Moreover, w can be a palindrome iff • the first and last characters of w are the same • w-first-last is also a palindrome

  30. Finding a base case • There are two possibilities for our test string • It may have an even number of characters • It may have an odd number of characters • This should signal to you that there will be two base cases • If w.length is even, then we can strip away pairs of letters checking that first=last until we reach the empty string (which is automatically a palindrome) • If w.length is odd, then we can strip away pairs of letters checking that first=last until we reach a single character (which is automatically a palindrome)

  31. A grammar for palindromes • This gives us the following grammar: <palindrome>=empty string | <character> | a<palindrome>a | b<palindrome>b |… <character>=a|b|c|… • Based on this grammar, we can construct a recursive-valued function for recognizing palindromes

  32. Some pseudo-code isPalidrome(in w:string):boolean //Returns true if w is palindrome, //else returns false if (w is empty string or w.length = 1) return true else if (w.first = w.last) return isPalindrome(w-first-last) else return false

  33. Another example • Now let us consider a slightly more complicated example: strings of the form A^nB^n • This is a string of n consecutive As followed by n consecutive Bs • L=[w:w is of the form A^nB^n for some n>0] • The grammar is very similar to that for palindromes • You must strip away the first character to see if it is an A and strip away the last character to see if it is a B • <legal>=empty string|A<legal>B

  34. More pseudo-code isAnBn(in w:string):boolean //Returns true if w is of the form A^nB^n, else returns false if (w is empty string) return true else if (w.first=A and w.last=B) return isAnBn(w-first-last) else return false

  35. Algebraic expressions • One of the primary tasks of a compiler is to recognize and evaluate algebraic expressions • A C++ compiler must determine if the right-hand side of the equation is a syntactically legal algebraic expression • If not, generate an error • If so, determine its value

  36. Parentheses, parentheses everywhere, but not an expression to evaluate • Some expression definitions require that the user fully parenthesize their expressions • Eliminates ambiguity • Simplifies parser coding • Must write ((a*b)*c) instead of a*b*c • The stricter the definition, the easier to recognize a legal statement • The looser the definition, the easier for your users (developers)

  37. Some examples • The languages we’re about to cover are easy to recognize and evaluate • Prefix • Infix • Postfix • However, they are generally inconvenient to use • They are good, non-trivial examples of how to use grammars

  38. Infix expression • The algebraic expressions with which you are most familiar are those you learned in your secondary education • These expressions are called infix expressions, which implies that binary operators occur between the operands • For example, a+b • The operator (+) appears between the operands (a,b)

  39. More on infix • There are associativity and precedence rules to eliminate the ambiguity inherent with this type of expression • Without these rules, what would be the correct evaluation of a+b*c? • With our rules, * is a higher precedence than + • So we have a+(b*c), where (b*c) is the second operand of the addition

  40. Still more on infix • Even with precedence it is difficult to evaluate a/b*c • We know / and * have equal precedence • However, we associate from left to right, so the correct expression is (a/b)*c • All this is a bit cumbersome for the grammar interpreter, but convenient for the grammar user

  41. Alternatives to infix • We have two alternatives to the infix expression a+b • Postfix, where the operator goes after the operands ab+ • Prefix, where the operator goes before the operands +ab • These expression eliminate the need for precedence and associativity rules!

  42. a+b*c • Fully parenthesized, this expression would be a+(b*c) • The prefix expression becomes +a*bc • The postfix expression becomes *+abc

  43. (a+b)*c • In prefix form, *+abc • In postfix form, ab+c* • In general, if the infix expression is fully parenthesized, converting to either prefix or postfix is fairly straightforward

  44. ((a+b)*c) • To convert this expression to prefix form, we move each operator to the position marked by its corresponding open parenthesis • Next, we remove all parenthesis to leave us with *+abc • Voila!

  45. ((a+b)*c) redux • We employ a similar strategy to convert this expression to postfix form • We move the operators to their corresponding close parenthesis • We then remove all parenthesis, leaving ab+c* • Voila!

  46. Prefix/Postfix vs. Infix • As stated earlier, prefix/postfix remove the stricture of defining precedence and association rules for our grammar • The grammars are therefore very straightforward and simple • Moreover, the recognition algorithms for these expressions are equally facile.

  47. Prefix expressions • A grammar for all prefix expressions: <prefix> = <identifier>|<operator><prefix><prefix> <operator> = + | - | * | / <identifier> = A|B|C|D|…|Z • If a string length is greater than 1, the expression must be of the form <operator><prefix><prefix> to be considered legal

  48. Prefix recognition algorithm • The recognition algorithm must check that • The first character of the string is an operator • Subsequent characters must be two prefix expressions • The first task is trivial, but the second is a bit tricky • Difficult to recognize where one prefix expression ends and the other begins • Can resolve this problem with spaces, commas, tabs…

  49. Evaluating prefix expressions • Since each operator is followed by its two operands, you can “scan ahead” in the expression for them • However, such operands can be prefix expressions as well which must be evaluated first • A recursive solution is quite natural for this problem

  50. Pseudo-code for prefix expression evaluatePrefix(in strExp:string):float //Evaluates a prefix expression string to its //numeric equivalent //Precondition: strExp has a valid prefix expression //Postcondition: returns numerical value of prefix //expression ch = first character of strExp Delete first character from strExp if (ch is an identifier) return value of identifier //base case else if (ch is operator called op) operand1 = evaluatePrefix(strExp) operand2 = evaluatePrefix(strExp) return operand1 op operand2

More Related