方案中的数独求解器?

时间:2013-11-06 23:31:53

标签: scheme

我们在课堂上为方案制作了一个矩阵库(文档在这里: http://www.cs.indiana.edu/cgi-pub/c211/wombat/docs/c211-matrix.htm

因此,作为一个我决定使用矩阵的小项目是一个数独求解器。 (这不是为了信用,这是在测试之前作为矩阵的一种练习)。

到目前为止,我已经完成了大部分程序,我只是坚持了几个最终功能。

我想编写一个名为check-block的函数,它将西北角的一行带到一个西北角的列和一个值,然后检查该值是否可以进入该块。所以基本上是其中一个Sudoku 3X3盒的左上角,它需要检查数字是否已经准备就绪。我在下面有一个check-row和check-col函数,它检查每个特定的行和列,看看是否有一个数字可以去那里,但它每次从第一列或正方形开始,不知道如何开始来自特定的西北角。

它的开头是:

(define (check-block nwr nwc val))

我假设我必须使用某种循环来检查块的每个部分。

在此之后我想写一些有效的东西?它采用行索引r,列索引c和值val,并检查是否可以将值放在给定位置。

这两个我真的被困住了,然后结束它我知道如何把它看作算法的立场。我需要另外四个函数solve,try-row,try-cell和try-value。

这个想法是解决只是调用try-row 0开始从第0行填充拼图。过程try-row假定所有先前的行都已正确填充,如果拼图未完成,则尝试进行通过调用(try-cell r 0)。过程try-cell假定所有先前的行和左侧的列都已正确填充。如果当前行已填满,则进入下一行。如果当前单元格不是空白,则会跳过它。否则,如果当前单元格为空,则调用(try-value rc 1),它尝试用1填充当前单元格。过程try-value将单元格的坐标和值v放在给定位置。如果该值超过9,则失败的过程返回。如果可以放置给定值,则该过程尝试下一个值。如果可以放置给定值,则修改板,并且通过尝试填充下一个单元来进行计算。如果尝试填充下一个失败,则删除添加的值,并尝试下一个值。

所以这是我到目前为止的代码:

;This function defines an empty cell as _
(define empty #\_)

;This makes it so there are 9 rows in the matrix
(define rows 9)

;This makes it so there are 9 columns in the matrix
(define cols 9)

;This makes it soa block size is considered 3X3
(define block-size 3)

;This makes board be the matri
(define board (make-matrix rows cols empty))

;This function physically builds the matrix
(define read-puzzle
   (lambda (fname)
     (with-input-from-file
       fname
       (lambda ()
         (let row-loop ([r 0])
           (unless (= r rows)
             (let col-loop ([c 0])
               (if (= c cols)
                   (row-loop (add1 r))
                   (begin
                     (matrix-set! board r c (read-cell))
                     (col-loop (add1 c)))))))))))

;This reads what cell has what value
(define read-cell
   (lambda () (let ([c (read)]) (if (eq? c '-) empty c))))

;This function checks a specific cell to see if it is blank or has a value
(define (blank? r c)
  (equal? empty (matrix-ref board r c)))

;This clears the board to an empty 9x9 matrix
(define (clear-board)
  (set! board (make-matrix rows cols empty)))

;This function checks if the value given can be put in that row by checking
;if that value all ready occurs in that row giving #t if it doesnt occur and
;#f if it does occur
(define (check-row r val)
  (define (cr-helper r c val)
    (cond
      [(>= c cols) #t]
      [(equal? val (matrix-ref board r c)) #f]
      [else (cr-helper r (add1 c) val)]))
   (cr-helper r 0 val))

;This function checks if the value given can be put in that column by checking
;if that value all ready occurs in that column giving #t if it doesnt occur and
;#f if it does occur
(define (check-col c val)
 (define (cc-helper r c val)
   (cond
     [(>= r rows) #t]
     [(equal? val (matrix-ref board r c)) #f]
     [else (cc-helper (add1 r) c val)]))
  (cc-helper 0 c val))

1 个答案:

答案 0 :(得分:2)

你的check-block函数需要两个嵌套循环,一个循环按行前进,一个循环按列向前推进,从西北角开始。两个循环中的每一个都从西北角以0,1或2的偏移量检查单元格,如果它与目标数字相同则返回#f。

我根据列表而不是矩阵编写了一个有点不同的数独求解器。我将重复下面的代码;你可以在my blog看到解释。​​

(define (sudoku puzzle)
  (define (safe? filled digit cell)
    (cond ((null? filled) #t)
          ((and (= (vector-ref (car filled) 0) (vector-ref cell 0))
                (char=? (vector-ref (car filled) 3) digit)) #f)
          ((and (= (vector-ref (car filled) 1) (vector-ref cell 1))
                (char=? (vector-ref (car filled) 3) digit)) #f)
          ((and (= (vector-ref (car filled) 2) (vector-ref cell 2))
                (char=? (vector-ref (car filled) 3) digit)) #f)
          (else (safe? (cdr filled) digit cell))))
  (define (next digit) (integer->char (+ (char->integer digit) 1)))
  (define (new old digit) (vector (vector-ref old 0) (vector-ref old 1) (vector-ref old 2) digit))
  (let scan ((s 0) (empty '()) (filled '()))
    (if (< s 81)
        (let* ((row (quotient s 9))
               (col (modulo s 9))
               (box (+ (* (quotient row 3) 3) (quotient col 3)))
               (digit (string-ref puzzle s))
               (cell (vector row col box digit)))
          (if (char=? digit #\0)
              (scan (+ s 1) (cons cell empty) filled)
              (scan (+ s 1) empty (cons cell filled))))
        (let solve ((empty empty) (filled filled))
          (if (pair? empty)
              (let try ((cell (car empty)) (digit (next (vector-ref (car empty) 3))))
                (cond ((char<? #\9 digit) ; backtrack
                        (solve (cons (car filled) (cons (new cell #\0) (cdr empty))) (cdr filled)))
                      ((safe? filled digit cell) ; advance
                        (solve (cdr empty) (cons (new cell digit) filled)))
                      (else (try cell (next digit))))) ; try next digit
              (let ((str (make-string 81 #\0)))
                (do ((filled filled (cdr filled))) ((null? filled) str)
                  (let* ((cell (car filled)) (s (+ (* (vector-ref cell 0) 9) (vector-ref cell 1))))
                    (string-set! str s (vector-ref cell 3))))))))))

数独谜题以行 - 主要顺序表示为81个字符的字符串,空单元格表示为零。这是一个示例解决方案:

> (sudoku "700100000020000015000006390200018000040090070000750003078500000560000040000001002")
"789135624623947815451286397237418569845693271916752483178524936562379148394861752"

您可以在 http://programmingpraxis.codepad.org/PB1czuyF运行该程序。 您可能还喜欢matrix library中的Standard Prelude