方案,N-queens优化策略SICP第2章

时间:2013-06-09 14:20:05

标签: lisp scheme sicp mit-scheme n-queens

SICP包含一个部分完整的n-queens解决方案示例,通过在最后一行中移动每个可能的皇后位置的树,在下一行中生成更多可能的位置以组合到目前为止的结果,过滤保留的可能性只有最新的女王是安全的,并递归重复。

这个策略在大约n = 11之后爆发,最大递归误差。

我已经实现了一个替代策略,它从第一列执行更智能的树步行,从未使用的行列表生成可能的位置,将每个位置列表放到尚未使用的行的更新列表中。过滤那些被认为是安全的对,并递归映射这些对以用于下一列。这不会爆炸(到目前为止),但n = 12需要一分钟,n = 13需要大约10分钟才能解决。

(define (queens board-size)
 (let loop ((k 1) (pp-pair (cons '() (enumerate-interval 1 board-size))))
   (let ((position (car pp-pair))
         (potential-rows (cdr pp-pair)))
    (if (> k board-size) 
        (list position)
        (flatmap (lambda (pp-pair) (loop (++ k) pp-pair)) 
         (filter (lambda (pp-pair) (safe? k (car pp-pair))) ;keep only safe
          (map (lambda (new-row) 
                (cons (adjoin-position new-row k position) 
                      (remove-row new-row potential-rows))) ;make pp-pair
           potential-rows))))))) 
;auxiliary functions not listed 

不是真正寻找代码,而是对一两个策略的简单解释,这些策略不那么天真,并且通过功能方法可以很好地点击。

2 个答案:

答案 0 :(得分:3)

我可以为您提供代码的简化,因此可以更快地运行。我们首先重命名一些变量以提高可读性(YMMV),

(define (queens board-size)
 (let loop ((k 1) (pd (cons () (enumerate-interval 1 board-size))))
   (let ((position (car pd))
         (domain   (cdr pd)))
    (if (> k board-size) 
        (list position)
        (flatmap (lambda (pd) (loop (1+ k) pd)) 
         (filter (lambda (pd) (safe? k (car pd))) ;keep only safe NewPositions
          (map (lambda (row) 
                (cons (adjoin-position row k position)  ;NewPosition
                      (remove-row row domain))) ;make new PD for each Row in D
               domain)))))))                            ; D

现在, filter f (map g d) == flatmap (\x->let y=g x in [y|f y]) d (在那里使用了一些Haskell语法),即我们可以融合 {{1}将map合并为一个filter

flatmap

然后, (flatmap (lambda (pd) (loop (1+ k) pd)) (flatmap (lambda (row) ;keep only safe NewPositions (let ( (p (adjoin-position row k position)) (d (remove-row row domain))) (if (safe? k p) (list (cons p d)) ()))) domain)) (其中flatmap h (flatmap g d) == flatmap (h <=< g) d是从右到左的Kleisli组合运算符,但谁在乎),所以我们可以融合两个<=<只有一个,

flatmap

所以简化的代码是

        (flatmap 
            (lambda (row)                         ;keep only safe NewPositions
                (let ((p (adjoin-position row k position)))
                  (if (safe? k p)
                    (loop (1+ k) (cons p (remove-row row domain)))
                    ())))
            domain)

答案 1 :(得分:1)

这是我第二次想出来的。不确定它的速度要快得多。虽然相当漂亮。

(define (n-queens n)
  (let loop ((k 1) (r 1) (dangers (starting-dangers n)) (res '()) (solutions '()))
    (cond ((> k n) (cons res solutions))
          ((> r n) solutions)
          ((safe? r k dangers) 
           (let ((this (loop (+ k 1) 1 (update-dangers r k dangers) 
                             (cons (cons r k) res) solutions)))
             (loop k (+ r 1) dangers res this)))
          (else (loop k (+ r 1) dangers res solutions)))))

重要的是使用let语句来序列化递归,将深度限制为n。解决方案向后出现(可能通过在r和k上转n-> 1而不是1&gt; n来修复)但是向后设置与frowards设置相同。

(define (starting-dangers n)
  (list (list)
        (list (- n))
        (list (+ (* 2 n) 1))))
;;instead of terminating in null list, terminate in term that cant threaten
小的改进,危险可能来自一排,一条向下的对角线,或一条向上的对角线,随着电路板的发展跟踪每一条。

(define (safe? r k dangers)
   (and (let loop ((rdangers (rdang dangers)))
           (cond ((null? rdangers) #t)
                 ((= r (car rdangers))
                  #f)
                 (else (loop (cdr rdangers)))))
        (let ((ddiag (- k r)))
           (let loop ((ddangers (ddang dangers)))
              (if (<= (car ddangers) ddiag)
                  (if (= (car ddangers) ddiag)
                      #f
                      #t)
                  (loop (cdr ddangers)))))
        (let ((udiag (+ k r)))
           (let loop ((udangers (udang dangers)))
              (if (>= (car udangers) udiag)
                  (if (= (car udangers) udiag)
                      #f
                      #t)
                  (loop (cdr udangers)))))))

格式变化的中等改进,只需要进行一次比较以检查与之前的两种。不要认为keeiping diagonals排序花了我任何东西,但我不认为它节省时间。

(define (update-dangers r k dangers)
  (list
     (cons r (rdang dangers))
     (insert (- k r) (ddang dangers) >)
     (insert (+ k r) (udang dangers) <))) 

 (define (insert x sL pred)
   (let loop ((L sL))
      (cond ((null? L) (list x))
            ((pred x (car L))
             (cons x L))
            (else (cons (car L)
                        (loop (cdr L)))))))

(define (rdang dangers)
  (car dangers))
(define (ddang dangers)
  (cadr dangers))
(define (udang dangers)
  (caddr dangers))