1 / 50

Backtracking CENG 707 Data Structures and Algorithms Yusuf Sahillio ğ lu

Dive into backtracking using stacks and trees to solve puzzles like crosswords, Sudoku, and the 8-queens puzzle. Learn the concept and implementation of backtracking with examples and algorithms. Study how to trace and solve puzzles effectively using recursion or stacks.

rjosue
Download Presentation

Backtracking CENG 707 Data Structures and Algorithms Yusuf Sahillio ğ lu

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. Backtracking CENG 707 Data Structures and Algorithms Yusuf Sahillioğlu 1

  2. Backtracking • Stacks and trees can be utilized to solve problems such as crosswords, Sudoku, 8-queens puzzle, and many other puzzles

  3. Backtracking • Stacks and trees can be utilized to solve problems such as crosswords, Sudoku, 8-queens puzzle, and many other puzzles • You are faced w/ a set of options, one of which must be chosen • After you make the choice, you get a new set accordingly • Repeat until you reach a final state • If your choices were good, your final state is a goal state • Else, it is not a goal state

  4. Backtracking • Stacks and trees can be utilized to solve problems such as crosswords, Sudoku, 8-queens puzzle, and many other puzzles • In other words, • Each choice (tree children) is recorded (in a stack) • When you run out of choices for the current decision, you pop the stack, and continue trying different choices for the previous decision

  5. Backtracking • Conceptually you start at the root of a tree • Tree has some good some bad leaves (all may be good/bad) • You want to get to a good leaf • At each node, beginning w/ root, you choose one of its children to move to, and repeat ‘till you get to a leaf • Suppose you get to a bad leaf • Do backtrack to continue the search for a good leaf by canceling your most recent choice (pop stack) and trying out the next option in that set of options • Recursion or stack • If you end up at the root w/ no options left, no good leaf 

  6. Backtracking • Starting at Root, your options are A and B. You choose A. • At A, your options are C and D. You choose C. • C is bad. Go back to A. • At A, you have already tried C, and it failed. Try D. • D is bad. Go back to A. • At A, you have no options left to try. Go back to Root. • At Root, you have already tried A. Try B. • At B, your options are E and F. Try E. • E is good. Congratulations!

  7. Backtracking • Generic backtracking algorithm w/ recursion boolean solve(Node n) { if n is a leaf node { if the leaf is a goal node, return true else return false } else { for each child c of n if solve(c) succeeds, return true return false } }

  8. Backtracking • Generic backtracking algorithm w/o recursion (w/ stacks) boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  9. Backtracking • Example puzzle: Balls on the move • n black balls, n white balls, 2n+1 spaces, middle empty • Represent the board as an array for the coding: 1,1,-1,2,2 (left) • Blacks/whites can only move to right/left, no backing up • A ball can • move 1 space ahead, if clear • Jump over 1 opposite-color ball, if clear

  10. Backtracking • Example puzzle: Balls on the move • A stuck/bad leaf

  11. Backtracking • Example puzzle: Balls on the move • Each move will yield new boards, which become the children of the current board • After some observation on ball movements • bool canMove(int[] board, int idx) • If idx is empty, no move possible (return false) • If idx contains a black ball, check for a valid move to go right • If idx contains a white ball, check for a valid move to go left

  12. Backtracking • Example puzzle: Balls on the move • Each move will yield new boards, which become the children of the current board • After some observation on ball movements • int[] makeMove(int[] oldBoard, int idx) • Make a move from idx on oldBoard and return the new board

  13. Backtracking • Example puzzle: Balls on the move • Resulting recursive code boolean solvable(int[] board) { if (puzzleSolved(board)) { return true; } for (int position = 0; position < BOARD_SIZE; position++) { if (canMove(board, position)) { int[] newBoard = makeMove(board, position); if (solvable(newBoard)) { printBoard(newBoard); return true; } } } return false;}

  14. Backtracking • Example puzzle: Balls on the move • Output of the recursive code (notice the reverse order)

  15. Backtracking • Example puzzle: Balls on the move • Resulting non-recursive code boolean solvable(int[] board) { //This is part of your homework # 2; see the next slides for help }

  16. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  17. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  18. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  19. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  20. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  21. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  22. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  23. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  24. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  25. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  26. Backtracking • Let’s see the execution trace along with the Stack boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  27. Backtracking • Let’s see the execution trace along with the Stack • M is a good leaf  boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  28. Backtracking • Let’s see the execution trace along with the Stack • Assume M was a bad leaf • backtrace ‘till G (most recent unmarked node) • G pushes O and N, which later push P and R boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S } return false }

  29. Backtracking • N-queen problem: safely place N queens on an NxN chessboard • Safe: no 2 queens on the same row, same column, same diagonal • N=8 case for the ordinary chessboard:

  30. Backtracking bool isSafe(int i, int j) //return True if Q can be placed at row i, col j //x[i] global array w/ first i-1 entries set already: x[a]=b //means that for row a, Q is safely sitting at col b for k = 1 to i-1 //check rows if (x[k] == j OR //same column |k-i| == |x[k]-j| //same diagonal (deltaX=deltaY, slope=1) return False return True (i,j) (k,l) Same diagonal since |i-k|=|j-l|

  31. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 : remaining j’s for this i

  32. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 : remaining j’s for this i

  33. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 no safe column : remaining j’s for this i

  34. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 i=2 : remaining j’s for this i remaining j suggested a new safe place, and ..

  35. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 i=2 i=3 : remaining j’s for this i remaining j suggested a new safe place, and called i=3 again

  36. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 i=2 i=3 i=4 no safe column

  37. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 i=2 i=3 i=4 i=3 no safe column

  38. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 i=2 i=3 i=4 i=3 i=2 no remaining left

  39. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 i=2 i=3 i=4 i=3 i=2 i=1 next safe cell

  40. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 i=2 i=3 i=4 i=3 i=2 i=1 i=2

  41. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 i=2 i=3 i=4 i=3 i=2 i=1 i=3

  42. Backtracking void NQueens(int i, int n) //initial call’ll be NQueens(1, N) for j = 1 to n //check columns if (isSafe(i, j)) x[i] = j if (i == n) printResult(x); //done! else NQueens(i+1, n) i=1 i=2 i=3 i=2 i=3 i=4 i=3 i=2 i=1 i=4 printResult()

  43. Backtracking • Let’s see the execution trace along with the Stack • Recall the generic algorithm boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S return false }

  44. Backtracking • Let’s see the execution trace along with the Stack bad because I cannot do the next row boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S return false }

  45. Backtracking • Let’s see the execution trace along with the Stack (cont’d) boolean solve(Node n) { push node n on the stack S; while S is not empty { if S.top() node is a leaf { if it is a goal node, return true else pop it off S } else if S.top() node is unmarked { Mark S.top() node for each child c of S.top() node push c to S } else //not a leaf and marked (we are backing out) pop it off S return false }

  46. Backtracking • Tree is just for visualization; you don’t have to use Tree in code • Algorithm with a Stack is as follow F=1 //# of filled rows F=2

  47. Backtracking • Tree is just for visualization; you don’t have to use Tree in code • Algorithm with a Stack is as follow F=2 //pop; F--; found 2,4; F++ //(continue from where you left off: 2,3) F=3

  48. Backtracking • Tree is just for visualization; you don’t have to use Tree in code • Algorithm with a Stack is as follow F=1 //pop; F--; try 3,3 and 3,4 in r3; unsafe //pop; F--; no where to try in row2; //pop; F--; found 1,2 in row1; F++; F=2

  49. Backtracking • Tree is just for visualization; you don’t have to use Tree in code • Algorithm with a Stack is as follow F=3 F=4; F reached N (4x4 board); finish

  50. Backtracking • Tree is just for visualization; you don’t have to use Tree in code • Notice how similar this execution trace to our Tree visualization F=4; F reached N (4x4 board); finish

More Related