CS 227 Lecture -*- Outline -*- Bring: pennies for the 8 queens problem. * Two Famous Problems (6.5) ** The Tower of Hanoi read description on page 178 and show picture Our problem is two-fold -- how to move the disks -- how to show the moves --- either return a list or --- print them out as they "happen" *** The recursion To move n disks from source to dest. using help post. e.g., from L to R using C as a help. Base: for moving one disk, just do it Recursion: (Suppose we know how to move n-1 disks, for n > 1) 1. Move top n-1 disks to help post (C) (using dest (R) as helper) 2. Move top largest disk to dest post (L to R) 3. Move n-1 disks from help post (C) to dest, using source (L) as helper. *** Showing the moves **** Lists Use "pairs" (source destination), i.e., 2 element lists e.g., (L C) moves L post to C. Use list of "pairs" for a sequence of moves ( (L C) (L R) (C R) ) to move from L to C, then L to C, then L to R, then C to R, each of these being the top disk. ---------------- ;; Program 6.9 <>= (define tower-of-hanoi ; TYPE: (-> (integer) (list moves)) (lambda (n) ; REQUIRES: n > 0 (letrec (<>) (move n 'L 'R 'C)))) <>= (move ; TYPE: (-> (number symbol symbol symbol) ; (list moves)) (lambda (n source destination helper) (if (= n 1) (list (list source destination)) (append (move (sub1 n) source helper destination) (cons (list source destination) (move (sub1 n) helper destination source)) )))) @ ---------------- try this with n = 3, etc. trace this on the board as follows (tower-of-hanoi 2) = (move 2 'L 'R 'C) = (append (move 1 'L 'C 'R) (cons (list 'L 'R) (move 1 'C 'R 'L))) = (append (move 1 'L 'C 'R) (cons (list 'L 'R) (list 'C 'R))) = (append (list 'L 'C) (cons (list 'L 'R) (list 'C 'R))) try Scheme trace for 3 **** Printing moves as generated ---------------- ;; Program 6.10 <>= (define display-tower-of-hanoi ; TYPE: (-> (number) void) (let (<>) (lambda (n) (letrec (<>) (move n 'L 'R 'C))))) <>= (show-move ;TYPE:(-> (symbol symbol) void) (lambda (s d) (display s) (display " -> ") (display d))) <>= (move ; TYPE: (-> (number symbol symbol symbol) ; void) (lambda (n source destination helper) (if (= n 1) (begin (show-move source destination) (newline)) (begin (move (sub1 n) source helper destination) (show-move source destination) (display ", ") (move (sub1 n) helper destination source) )))) @ ---------------- try this with n = 3, 4, etc. Point out the two places where the printing happens. Point out where the newline happens. They should think about why the moves are printed out in the same order and what happens for n=2 or n=4. trace as follows (tower-of-hanoi 2) = (move 2 'L 'R 'C) = (begin (move 1 'L 'C 'R) (show-move 'L 'C) (display ", ") (move 1 'C 'R 'L)) = (begin (begin (show-move 'L 'C) (newline)) (show-move 'L 'C) (display ", ") (move 1 'C 'R 'L)) = (begin (begin (show-move 'L 'C) (newline)) (show-move 'L 'C) (display ", ") (begin (show-move 'C 'R) (newline))) ** Eight Queens The problem is to place 8 queens on a chessboard so that none attacks the other. Show (or draw) Figure 6.11. -- A queen attacks another on horizontal, vertical, or diagonal lines. *** Data Structures Could produce graphical output, but easier to use numbers. Number rows and columns from lower left, 1--8. **** So could represent positions of queens as pairs: (row column) and board as a list of such pairs. **** Can compress this since no 2 queens can be in the same column, can use position in the list to represent the columns, as in how it looks, and thus only need numbers for rows. (5 7 2 6 3 1 4 8) (reading from right): represents queen in column 8, row 8, column 7, row 4, etc. **** Sub-solutions (position lists) The exact column numbers do not matter, since positions in a list are relative. Think of the last position in a list as for column 8 (or last column on smaller board). So (1 4 8) is a position list. **** Legal position Def: a position list is legal iff no queen attacks another in the list. Q: Is a one-element list legal? Def: a solution is a legal position list with the same size as the board (8). *** Backtracking Now how to solve the problem? No obvious recursive solution. So like square root, guess, and improve the guess. **** How? Can put queen in column 8, row 8 Where to put column 7 queen? 8 does not work, 7 does not work. 6 is ok. So partial solution is (6 8). Where to put column 6 queen? Not 8,7,6,5, but 4 is ok. Column 5? Not 8, but 7 works. (easy, eh?) Column 4? Not 8,7,6, but 5 works. Column 3? Not 8,7,6,5,4,3,2, or 1 !! **** Now what? -- We have a guess for queens in the columns. 8, 7, 6, 5 and 4 but it is wrong. -- We need to improve it. -- We can revise the last thing we did and try again, that is *backtracking* Similar to what people do: e.g., trying to find a major Decide to go to college, ISU, Engineering, Computer Engineering. May switch to EE, then out of engineering, etc. **** Backtracking Def: Revise last decision, but do not revisit already failed ideas. -- So column 4 queen, do not retry 8,7,6 again. (Keep track by always decreasing number). try rows: 4,3,2, 1 works. -- Now try column 3 queen again. 8,7,6,5,4,3,2,1 all rows fail. -- So backtrack again. Now no place left for column 4. -- So column 4 fails, backtrack to queen in column 5 --- that must not be right. -- Reconsider that one if they want it. *** Code Very subtle, read it several times with the text. 3 procedures: build-solution, forward and backtrack. Will discuss whatever we do not cover here in recitations. **** Build solution argument is a legal position list (starts at length 0) returns solution if done, otherwise, calls forward, to try next column. ;; Program 6.13 ---------------- (define build-solution ; TYPE: (-> (pos-list) pos-list) (lambda (legal-pl) ; REQUIRES: legal-pl is legal (cond ((solution? legal-pl) legal-pl) (else (forward fresh-try legal-pl))) )) ---------------- **** forward Backtracks when the row number (try) is zero. When the row number is a legal spot for the queen uses build-solution to work on the next column to the left. otherwise, tries the next row. (so basically a loop from 8 to zero). ;; Program 6.14 ---------------- (define forward ; TYPE: (-> (number pos-list) pos-list) (lambda (try legal-pl) ; REQUIRES: try >= 0 ; and legal-pl is legal (cond ((zero? try) (backtrack legal-pl)) ((legal? try legal-pl) (build-solution (cons try legal-pl))) (else (forward (sub1 try) legal-pl))))) ---------------- **** backtrack We use this when the solution failed, so the idea is to try again with the previous column. That is go forward with the queen in the car of the list. If there is no car, nothing else can be done, so really fail. ;; Program 6.15 ---------------- (define backtrack ; TYPE: (-> (pos-list) pos-list) (lambda (legal-pl) ; REQUIRES: legal-pl is legal (cond ((null? legal-pl) '()) (else (forward (sub1 (car legal-pl)) (cdr legal-pl)))))) ---------------- *** getting more solutions think of a solution as a failure, and backtrack over it (try another row for column 1). *** tracing it play with copy edited as in pages 187--188.