Download presentation
1
Solving N-Queens in Clojure
2
The N-Queens Problem The classic 8-queens problem is that of placing 8 queens on a chessboard so that no pair is attacking. Franz Nauck in 1850 extended the chess problem to n-queens problem on an n×n board. S. Günther proposed a method of finding solutions by using matrix determinants. Edsger Dijkstra used this problem in 1972 to illustrate the power of what he called structured programming.
3
Solution Design Let us represent solution using vectors of column locations. [ ] is the solution above We can write a generator that adds a queen to a new row as long as it is non-attacking. A simple recursive generator function n-queens will Take each solution to n-1 queens problem and apply map which-queen is function to computes list of possible additions (we’ll use list comprehensions/for simplicity) map the conjall using which queen to add to each partial solution So n_queens() will take 2 parameters, the number of queens n (same as # rows) and m (or number of cols) of the board.
4
Pattern for n-queens coming from allchains solution
(defn allchains [n m] (cond (= n 0) '(()) :else (apply concat (map (fn [it] (conjall (range 1 (inc m)) it)) (allchains (dec n) m))))) (defn n-queens [n m] (= n 0) '([]) (which-queens it m) it)) (n-queens (dec n) m))))
5
Recall conjall for generating collections
conj is the standard op for building collections in Clojure. conj returns a new collection with the new item 'added'. The 'addition' may happen at different 'places' depending on the concrete type. user=> (conj [1 2 3] 4) => [ ] user=> (conj '(1 2 3) 4) => ( ) We write conjall with input a vector vec and a list lst… and returns a collection of all conj’s of lst elements onto the vec. (defn conjall [lst vec] (cond (empty? lst) '() :else (conj (conj vec (first lst)) (conjall (rest lst1) vec))) ;(conjall '(4 5 6) [ 1 2 ]) ; => ([1 2 4] [1 2 5] [1 2 6])
6
List Comprehension using for
List comprehension uses for for generating lists. Takes a vector of one or more binding-form/collection-expr pairs, each followed by zero or more modifiers, and yields a lazy sequence of evaluations of expr. (for [x (range 6) y (range 5) :let [z (* x y)] :when (odd? z)] (list x y)) ;=> ((1 1) (1 3) (3 1) (3 3) (5 1) (5 3)) :when iterates over the bindings, but only evaluates the body of the loop when the condition is true. :while iterates over the bindings and evaluates the body until the condition is false: (for [x (range 20) :when (not= x 10)] x) ; =>( ) (for [x (range 20) :while (not= x 10)] x) ; => ( )
7
Which-queens to add? ; for each possible col x return x ; if all other queens in partial sol psol are non-attacking (defn which-queens [psol m] (for [x (range m) :when (not-any? true? (for [i (range (count psol)) :let [pi (psol i)]] (or ;check if pi and x share col or diagonal (= pi x) (= (- (count psol) i) (Math/abs (- x pi)))) )) ] x))
8
That all! 92 solutions (defn n-queens [n m] (cond (= n 0) '([]) :else
(apply concat (map (fn [it] (conjall (which-queens it m) it)) (n-queens (dec n) m)))) user=> (count (n-queens 8 8)) 92 user=> (n-queens 8 8) ([ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ])
9
Isomorph Rejection Problem
Not all of the 92 solutions found can be considered unique, in the sense that rotating or flipping the board around can result in another solution found in the set. 8 transformations that map the chess-board to itself; 4 rotations of 90 degrees, and 4 reflections --the so-called dihedral group D8 of automorphisms of the square. A solution (based on perfect hashing) is to consider each solution of N-Queens as a base N+1 number. We can generate solutions in numeric order, and test if a solution is isomorphic to a previously found solution if and only if one of the 8 transformations produces a solution, which is numerically (or, more generally, lexicographically) less than the original We can lexicographically compare solutions as follows… user=> (compare [ ] [ ]) -1
10
Working with immutability is sometimes difficult
(This may not be best work around) To work in an immutable fashion we will expand and collapse each vector using a list of [row-index col-index] pairs as intermediate solution. ; (def a (first (n-queens 8 8)) (defn expand [sol] (map vector (range 8) sol) ) ;(expand a) ;=>([0 7] [1 3] [2 0] [3 2] [4 5] [5 1] [6 6] [7 4]) If [i j] is a queen in sol, then [j 7-i] is a queen in (rotate sol) If [i j] is a queen in sol, then [i 7-j] is a queen in (reflect sol) (defn rotate [sol] ( map (fn[x] (let [[ i j] x] (vector j (- 7 i)))) sol)) (defn reflect [sol] ( map (fn[x] (let [[ i j] x] (vector i (- 7 j)))) sol))
11
Expand and Collapse Transformed Solutions
(reflect (expand a)) ;=> ([0 7] [1 3] [2 0] [3 2] [4 5] [5 1] [6 6] [7 4]) ; (sort (rotate (expand a))) ;=> ([0 7] [1 1] [2 3] [3 0] [4 6] [5 4] [6 2] [7 5]) (defn collapse [p] (into [] (map (fn[ij] (last ij)) p))) ;(collapse '([0 7] [1 1] [2 3] [3 0] [4 6] [5 4] [6 2] [7 5])) ;=> [ ]
12
into lets you take anything seq'able
Take a list, vector, map, set, sorted-map and an empty container you want filled. (into [] '( )) ==> [ ] "have a lazy list and want a vector" into #{} [ ]) ==> #{ } "have a vector and want a set" > (into {} #{[1 2] [3 4]}) ==> {3 4, 1 2} "have a set of vectors want a map" > (into #{} [{1 2} {3 4}]) ==> #{{1 2} {3 4}} "have a vector of maps want a set of maps"
13
Here is a potential solution
(defn non-iso [n] (filter canonical-pred? (n-queens n n))
14
Homework #3 How many orbits/non-iso solutions?
How many are full (size 8) and degenerate? Catalog the fixed configurations of 8 symmetries. Write clojure program to produce list of all canonical (non-isomorphic) solution vectors.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.