41
1 CSE 143 Lecture 18 Recursive Backtracking Based on slides by Marty Stepp & Ethan Apter

CSE 143 Lecture 18

  • Upload
    harlow

  • View
    38

  • Download
    4

Embed Size (px)

DESCRIPTION

CSE 143 Lecture 18. Recursive Backtracking Based on slides by Marty Stepp & Ethan Apter. Exercise. Write a method permute that accepts a string as a parameter and outputs all possible rearrangements of the letters in that string. The arrangements may be output in any order. - PowerPoint PPT Presentation

Citation preview

Page 1: CSE 143 Lecture 18

1

CSE 143Lecture 18

Recursive Backtracking

Based on slides by Marty Stepp & Ethan Apter

Page 2: CSE 143 Lecture 18

2

Exercise• Write a method permute that accepts a string as a

parameter and outputs all possible rearrangements of the letters in that string. The arrangements may be output in any order.– Example:permute("MARTY")outputs the followingsequence of lines:

MARTYMARYTMATRYMATYRMAYRTMAYTRMRATYMRAYTMRTAYMRTYAMRYATMRYTAMTARYMTAYRMTRAYMTRYAMTYARMTYRAMYARTMYATR MYRATMYRTAMYTARMYTRA

AMRTYAMRYTAMTRYAMTYRAMYRTAMYTRARMTYARMYTARTMYARTYMARYMTARYTMATMRYATMYRATRMYATRYMATYMRATYRMAYMRTAYMTRAYRMTAYRTMAYTMRAYTRM

RMATYRMAYTRMTAYRMTYARMYATRMYTARAMTYRAMYTRATMYRATYMRAYMTRAYTMRTMAYRTMYARTAMYRTAYMRTYMARTYAMRYMATRYMTARYAMTRYATMRYTMARYTAM

TMARYTMAYRTMRAYTMRYATMYARTMYRATAMRYTAMYRTARMYTARYMTAYMRTAYRMTRMAYTRMYATRAMYTRAYMTRYMATRYAMTYMARTYMRATYAMRTYARMTYRMATYRAM

YMARTYMATRYMRATYMRTAYMTARYMTRAYAMRTYAMTRYARMTYARTMYATMRYATRMYRMATYRMTAYRAMTYRATMYRTMAYRTAMYTMARYTMRAYTAMRYTARMYTRMAYTRAM

Page 3: CSE 143 Lecture 18

3

Examining the problem• Think of each permutation as a set of choices or decisions:

– Which character do I want to place first?– Which character do I want to place second?– ...– solution space: set of all possible sets of decisions to explore

• We want to generate all possible sequences of decisions.for (each possible first letter):

for (each possible second letter):for (each possible third letter):

...print!

– This is called a depth-first search

Page 4: CSE 143 Lecture 18

4

Decision Treeschosen available

M A R T Y

M A R T Y

M A R T Y

M A R T Y

M A R T Y

M A R T Y

M R A T Y M T A R Y M Y A R T

A M R T Y

M A T R Y M A Y R T

M A R Y T

M A R Y T

M A Y R T

M A Y R T

M A Y T R

M A Y T R

M Y A R T .........

...

... ...

Page 5: CSE 143 Lecture 18

5

Backtracking• backtracking: A general algorithm for finding

solution(s) to a computational problem by trying partial solutions and then abandoning them ("backtracking") if they are not suitable.– a "brute force" algorithmic technique (tries all paths; not

clever)– often (but not always) implemented recursively

Applications:– producing all permutations of a set of values– parsing languages– games: anagrams, crosswords, word jumbles, 8 queens– combinatorics and logic programming

Page 6: CSE 143 Lecture 18

6

Backtracking algorithmsA general pseudo-code algorithm for backtracking

problems:

explore(choices):– if there are no more choices to make: stop.– else:

•Make a single choice C from the set of choices.– Remove C from the set of choices.

•explore the remaining choices (given that you’ve made choice C).

•Un-make choice C.– Backtrack!

Page 7: CSE 143 Lecture 18

7

Backtracking strategies• When solving a backtracking problem, ask these questions:

– What are the "choices" in this problem?•What is the "base case"? (How do I know when I'm out of choices?)

– How do I "make" a choice?•Do I need to create additional variables to remember my choices?•Do I need to modify the values of existing variables?

– How do I explore the rest of the choices?•Do I need to remove the made choice from the list of choices?

– Once I'm done exploring the rest, what should I do?

– How do I "un-make" a choice?

Page 8: CSE 143 Lecture 18

8

Permutations revisited• Write a method permute that accepts a string as a

parameter and outputs all possible rearrangements of the letters in that string. The arrangements may be output in any order.– Example:permute("MARTY")outputs the followingsequence of lines:

MARTYMARYTMATRYMATYRMAYRTMAYTRMRATYMRAYTMRTAYMRTYAMRYATMRYTAMTARYMTAYRMTRAYMTRYAMTYARMTYRAMYARTMYATR MYRATMYRTAMYTARMYTRA

AMRTYAMRYTAMTRYAMTYRAMYRTAMYTRARMTYARMYTARTMYARTYMARYMTARYTMATMRYATMYRATRMYATRYMATYMRATYRMAYMRTAYMTRAYRMTAYRTMAYTMRAYTRM

RMATYRMAYTRMTAYRMTYARMYATRMYTARAMTYRAMYTRATMYRATYMRAYMTRAYTMRTMAYRTMYARTAMYRTAYMRTYMARTYAMRYMATRYMTARYAMTRYATMRYTMARYTAM

TMARYTMAYRTMRAYTMRYATMYARTMYRATAMRYTAMYRTARMYTARYMTAYMRTAYRMTRMAYTRMYATRAMYTRAYMTRYMATRYAMTYMARTYMRATYAMRTYARMTYRMATYRAM

YMARTYMATRYMRATYMRTAYMTARYMTRAYAMRTYAMTRYARMTYARTMYATMRYATRMYRMATYRMTAYRAMTYRATMYRTMAYRTAMYTMARYTMRAYTAMRYTARMYTRMAYTRAM

Page 9: CSE 143 Lecture 18

9

Permutations Exercise// Outputs all permutations of the given string.public static void permute(String s) { permute(s, "");}private static void permute(String avail, String chosen) { if ( ) { // base case: no choices left to be made

} else { // recursive case: choose each possible next letter

} }}

Page 10: CSE 143 Lecture 18

10

Permutations Exercise// Outputs all permutations of the given string.public static void permute(String s) { permute(s, "");}

private static void permute(String avail, String chosen) { if (avail.length() == 0) { // base case: no choices left to be made System.out.println(chosen); } else { // recursive case: choose each possible next letter for (int i = 0; i < avail.length(); i++) {

} }}

Page 11: CSE 143 Lecture 18

11

Permutations Exercise// Outputs all permutations of the given string.public static void permute(String s) { permute(s, "");}

private static void permute(String avail, String chosen) { if (avail.length() == 0) { // base case: no choices left to be made System.out.println(chosen); } else { // recursive case: choose each possible next letter for (int i = 0; i < avail.length(); i++) { String ch = avail.substring(i, i + 1); // choose ith

String rest = avail.substring(0, i) + // remove ith avail.substring(i + 1);

// explore } }}

Page 12: CSE 143 Lecture 18

12

Exercise solution// Outputs all permutations of the given string.public static void permute(String s) { permute(s, "");}

private static void permute(String avail, String chosen) { if (avail.length() == 0) { // base case: no choices left to be made System.out.println(chosen); } else { // recursive case: choose each possible next letter for (int i = 0; i < avail.length(); i++) { String ch = avail.substring(i, i + 1); // choose ith

String rest = avail.substring(0, i) + // remove ith avail.substring(i + 1);

permute(rest, chosen + ch); // explore } }}

Page 13: CSE 143 Lecture 18

13

8 Queens• 8 Queens is a classic backtracking problem

• 8 Queens: place 8 queens on an 8x8 chessboard so that no queen threatens another– queens can move in a straight line horizontally, vertically,

or diagonally any number of spaces

possible moves

threatened! safe!

Page 14: CSE 143 Lecture 18

14

Solve 8 Queens with Recursive Backtracking

• Consider the problem of trying to place 8 queens on a chess board such that no queen can attack another queen.

– What are the "choices"?

– How do we "make" or"un-make" a choice?

– How do we know whento stop?

QQ

QQ

QQ

QQ

Page 15: CSE 143 Lecture 18

15

Naive algorithm• for (each square on the board):

– Place a queen there.– Try to place the rest

of the queens.– Un-place the queen.

– How large is thesolution space forthis algorithm?

1 2 3 4 5 6 7 81 Q ... ... ... ... ... ... ...2 ... ... ... ... ... ... ... ...3 ...45678

Page 16: CSE 143 Lecture 18

16

8 Queens• One possible approach:

– on an 8x8 chessboard, there are 64 locations– each of these locations is a potential location to place the

first queen (this is a choice!)– after we place the first queen, there are 63 remaining

locations to place the second queen•clearly, some of these won’t work, because the second queen

will threaten the first queen.– after we place the second queen, there are 62 remaining

locations to place the third queen– and so on

• So, there are 178,462,987,637,760 possibilities!– 178,462,987,637,760 = 64*63*62*61*60*59*58*57

Page 17: CSE 143 Lecture 18

17

8 Queens• That’s a lot of choices!

• Remember that we’re using a brute-force technique, so we have to explore all possible choices– now you can really see why brute-force techniques are

slow!

• However, if we can refine our approach to make fewer choices, we can go faster– we want to be clever about our choices and make as few

choices as possible

• Fortunately we can do a lot better

Page 18: CSE 143 Lecture 18

18

Better algorithm idea• Observation: In a working solution, exactly 1 queen

must appear in each rowand in each column.

– Redefine a "choice"to be valid placementof a queen in aparticular column.

– How large is thesolution space now?

1 2 3 4 5 6 7 81 Q ... ...2 ... ...3 Q ...4 ...5 Q678

Page 19: CSE 143 Lecture 18

19

8 Queens• Key observation:

– all valid solutions to 8 Queens will have exactly 1 queen in each row and exactly 1 queen in each column (otherwise the queens must threaten each other)

• There are exactly 8 queens, 8 rows, and 8 columns

• So rather than exploring 1-queen-per-board-location, we can explore 1-queen-per-row or 1-queen-per-column– it doesn’t matter which

• We’ll explore 1-queen-per-column

Page 20: CSE 143 Lecture 18

20

8 Queens• When exploring column-by-column, we have to decide

which row to place the queen for a particular column

• There are 8 columns and 8 rows– so we’ve reduced our possible choices to 88 = 16,777,216

• So our first decision looks something like this:

for column #1, in which row should the queen be placed?

1 2 3 4 5 6 7 8

Page 21: CSE 143 Lecture 18

21

8 Queens• If we choose to place the queen in row #5, our decision

tree will look like this:

• Keep in mind that our second choice (column #2) is affected by our first choice (column #1)

for column #1, in which row should the queen be placed?

1 2 3 4 5 6 7 8

for column #2, in which row should the queen be placed?

1 2 3 4 5 6 7 8

Page 22: CSE 143 Lecture 18

22

8 Queens• So, why are we using backtracking?

– because backtracking allows us to undo previous choices if they turn out to be mistakes

• Notice that as we choose which row to place each queen, we don’t actually know if our choices will lead to a solution– we might be wrong!

• If we are wrong, we want to undo the minimum number of choices necessary to get back on the right track– this also saves as much previous work as possible

Page 23: CSE 143 Lecture 18

23

8 Queens• It’s clear that we could explore the possible choices for

the first column with a loop:

for (int row = 1; row <= 8; row++) // explore column 1

• So we could solve the whole problem with nested loops somewhat like this:

for (int row = 1; row <= 8; row++) // column 1 for (int row = 1; row <= 8; row++) // column 2 for (int row = 1; row <= 8; row++) // column 3 ... for (int row = 1; row <= 8; row++) // column 8

• But we can write this more elegantly with recursion

Page 24: CSE 143 Lecture 18

24

8 Queens• Recursive backtracking problems have somewhat of a

general form

• This form is easier to see (and the code is easier to understand) if we remove some of the low-level details from our recursive backtracking code

• To do this, we’ll be using some code Stuart Reges wrote. Stuart’s code is based off code written by one of his former colleagues named Steve Fisher

• We’re going to use this code to solve N Queens– just like 8 queens, but now we can have N queens on an

NxN board (where N is any positive number)

Page 25: CSE 143 Lecture 18

25

N Queens• What low-level methods do we need for N Queens?

– We need a constructor that takes a size (to specify N): public Board(int size)

– We need to know if it’s safe to place a queen at a location public boolean safe(int row, int col)

– We need to be able to place a queen on the board public void place(int row, int col)

– We need to be able to remove a queen from the board, because we might make mistakes and need to backtrack

public void remove(int row, int col)

– And we need some general information about the board public void print() public int size()

Page 26: CSE 143 Lecture 18

26

N Queens• Assume we have all the previous code

• With that taken care of, we just have to find a solution!– easy, right?

• Let’s write a method called solve to do this: public static void solve(Board b) { ... }

• Unfortunately, solve doesn’t have enough parameters for us to do our recursion– so let’s make a private helper method

Page 27: CSE 143 Lecture 18

27

N Queens• Our private helper method: private static boolean explore(...) { ... }

• What parameters does explore need?

Page 28: CSE 143 Lecture 18

28

N Queens• Our private helper method: private static boolean explore(...) { ... }

• What parameters does explore need?– it needs a Board to place queens on– it needs a column to explore

•this is a little tricky to see, but this will let each method invocation work on a different column

• Updated helper method: private static boolean explore(Board b, int col) { ... }

Page 29: CSE 143 Lecture 18

29

N Queens• Well, now what?

• We don’t want to waste our time exploring dead ends– so, if someone wants us to explore column #4, we should

require that columns #1, #2, and #3 all have queens and that these three queens don’t threaten each other

– we’ll make this a precondition (it’s a private method, after all)

• So, now our helper method has a precondition: // pre : queens have been safely placed in previous // columns

Page 30: CSE 143 Lecture 18

30

N Queens• Time to write our method// pre : queens have been safely placed in previous columnsprivate static boolean explore(Board b, int col) {

• We know it’s going to be recursive, so we need at least:– a base case– a recursive case

• Let’s think about the base case first

• What column would be nice to get to? When are we done?

Page 31: CSE 143 Lecture 18

31

N Queens• Time to write our method

• We know it’s going to be recursive, so we need at least:– a base case– a recursive case

• Let’s think about the base case first

• What column would be nice to get to? When are we done?– For 8 Queens, column 9 (queens 1 to 8 placed safely)

•column 8 is almost correct, but remember that if we’re asked to explore column 8, the 8th queen hasn’t yet been placed

– For N Queens, column N+1 (queens 1 to N placed safely)

Page 32: CSE 143 Lecture 18

32

N Queens• This is our base case!• Let’s update our helper code to include it:

// pre : queens have been safely placed in previous columnsprivate static boolean explore(Board b, int col) { if (col > b.size()) { return true; } else { ... } }

• Well, that was easy• What about our recursive case?

Page 33: CSE 143 Lecture 18

33

N Queens• For our recursive case, suppose we’ve already placed

queens in previous columns

• We want to try placing a queen in all possible rows for the current column

• We can try all possible rows using a simple for loop: for (int row = 1; row <= board.size(); row++) { ... }

• This is the same for loop from before!– remember, even though we’re using recursion, we still want

to use loops when appropriate

Page 34: CSE 143 Lecture 18

34

N Queens• When do we want to try placing a queen at a row for the

specified column?– only when it is safe to do so!– otherwise this location is already threatened and won’t lead

us to a solution

• We can update our code: for (int row = 1; row <= board.size(); row++) { if (b.safe(row, col)) { ... } }

• We’ve picked our location and determined that it’s safe– now what?

Page 35: CSE 143 Lecture 18

35

N Queens // pre : queens have been safely placed in previous columnsprivate static boolean explore(Board b, int col) {

if (col > b.size()) { return true; } else { for (int row = 1; row <= board.size(); row++) { if (b.safe(row, col)) {

} }

Page 36: CSE 143 Lecture 18

36

N Queens• We need to place a queen at this spot and decide if we

can reach a solution from here– if only we had a method that would explore a Board from

the next column and decide if there’s a solution...– oh wait! That’s what we’re writing

• We can update our code to place a queen and recurse: for (int row = 1; row <= board.size(); row++) { if (b.safe(row, col)) { b.place(row, col); explore(b, col + 1); ... } }

You might be tempted to write col++ here instead, but that won’t work. Why not?

Page 37: CSE 143 Lecture 18

37

N Queens• Also, we don’t want to call explore quite like that

– explore returns a boolean, telling us whether or not we succeeded in finding a solution (true if found, false otherwise)

• What should we do if explore returns true?– stop exploring and return true (a solution has been found)

• What should we do if explore returns false?– well, the queens we’ve placed so far don’t lead to a

solution– so, we should remove the queen we placed most recently

and try putting it somewhere else

Page 38: CSE 143 Lecture 18

38

N Queens• Updated code: for (int row = 1; row <= board.size(); row++) { if (b.safe(row, col)) { b.place(row, col); if (explore(b, col + 1)) { return true; } b.remove(row, col); } }

• We’re almost done. What should we do if we’ve tried placing a queen at every row for this column, and no location leads to a solution?– No solution exists, so we should return false

This pattern (make a choice, recurse, undo the choice) is really common in recursive backtracking

Page 39: CSE 143 Lecture 18

39

N Queens Solution• And we’re done! Here’s the final code for explore:// pre : queens have been safely placed in previous columnsprivate static boolean explore(Board b, int col) {

if (col > b.size()) { return true; } else { for (int row = 1; row <= board.size(); row++) { if (b.safe(row, col)) { b.place(row, col); if (explore(b, col + 1)) { return true; } b.remove(row, col); } } return false; } }

Page 40: CSE 143 Lecture 18

40

N Queens• Well, actually we still need to write solve

– don’t worry, it’s easy!

• We’ll have solve print out the solution if explore finds one. Otherwise, we’ll have it tell us there’s no solution

• Code for solve: public static void solve(Board b) { if (explore(b, 1)) { System.out.println(“One solution is as follows:”); b.print(); } else { System.out.println(“No solution”); } }

Page 41: CSE 143 Lecture 18

41

N Queens• We’re really done and everything works

– try running the code yourself!– I think it’s pretty cool that such succinct code can do so

much

• There’s also an animated version of the code– it shows the backtracking process in great detail– if you missed lecture (or if you just want to see the

animation again), download queens.zip from the class website and run Queens2.java