「 あらゆる数独パズルを解く」はPeter Norvigなのに、Pythonで書かれている。
じゃあというわけでelispで書いてみた。
(2) ある値に対し、ユニットの中で置ける場所が1つしかないなら、その値をそこに置く。
は、うまく動かなかったのでコメントアウトしているけど。
それでも「 あらゆる数独パズルを解く」のページの問題はいちおう解けたからだいじょうぶだと思うけど。
まぁ、よいか。
( defun test () ( progn (sudoku-init) (sudoku-set " . . 5 |3 . . |. . . 8 . . |. . . |. 2 . . 7 . |. 1 . |5 . . ------+------+------ 4 . . |. . 5 |3 . . . 1 . |. 7 . |. . 6 . . 3 |2 . . |. 8 . ------+------+------ . 6 . |5 . . |. . 9 . . 4 |. . . |. 3 . . . . |. . 9 |7 . . ") (sudoku-solve) (sudoku-print))) (benchmark 1 '(test)) | 1 4 5 | 3 2 7 | 6 9 8 | | 8 3 9 | 6 5 4 | 1 2 7 | | 6 7 2 | 9 1 8 | 5 4 3 | |-------+-------+-------| | 4 9 6 | 1 8 5 | 3 7 2 | | 2 1 8 | 4 7 3 | 9 5 6 | | 7 5 3 | 2 9 6 | 4 8 1 | |-------+-------+-------| | 3 6 7 | 5 4 2 | 8 1 9 | | 9 8 4 | 7 6 1 | 2 3 5 | | 5 2 1 | 8 3 9 | 7 6 4 | "Elapsed time: 0.179490s (0.093774s in 1 GCs)"
ベンチマークもそこそこ。
;; -*- coding: utf-8; lexical-binding: t -*- | |
;; | |
;; 数独(sudoku.el) | |
;; | |
(require 'cl) | |
(require 'org) | |
(require 'dash) | |
(defun sudoku-combination-of-list (list1 list2) | |
"list1の要素とlist2の要素を組み合わせた結果を出力する。" | |
(-table-flat 'concat list1 list2)) | |
(defun sudoku-table-convert (string) | |
"文字列をtableに変換する。" | |
(with-temp-buffer | |
(let ((w (replace-regexp-in-string "^\n" "" string))) | |
(insert w) | |
(org-table-convert-region (point-min)(point-max)) | |
(goto-char (point-min)) | |
(next-line 2) | |
(org-ctrl-c-minus) | |
(next-line 4) | |
(org-ctrl-c-minus) | |
(buffer-substring-no-properties (point-min)(point-max))))) | |
(let* ((tate '("1" "2" "3" "4" "5" "6" "7" "8" "9")) | |
(yoko '("A" "B" "C" "D" "E" "F" "G" "H" "I")) | |
(tate-group (-partition 3 tate)) | |
(yoko-group (-partition 3 yoko)) | |
(keys (sudoku-combination-of-list tate yoko)) | |
(hash (make-hash-table :size 81 :test 'equal))) | |
(defun sudoku-hash () | |
hash) | |
(defun sudoku-keys() keys) | |
(defun sudoku-init () | |
"初期化処理" | |
(loop for x in keys ; 初期設定 | |
do (puthash x "123456789" hash))) | |
(sudoku-init) ; 最初の初期化 | |
(defun sudoku-units (key) | |
"KEYはマス目の場所をあらわす。pearになるリストを返す。" | |
(let* ((tate-key (substring key 0 1)) | |
(yoko-key (substring key 1 2)) | |
(tate (loop for x in tate-group | |
when (cl-find tate-key x :test #'string=) | |
collect x)) | |
(yoko (loop for x in yoko-group | |
when (cl-find yoko-key x :test #'string=) | |
collect x))) | |
(sudoku-combination-of-list (car tate)(car yoko)))) | |
(defun sudoku-tate (key) | |
(let ((tate-key (substring key 0 1))) | |
(sudoku-combination-of-list (list tate-key) yoko))) | |
(defun sudoku-yoko (key) | |
(let ((yoko-key (substring key 1 2))) | |
(sudoku-combination-of-list tate (list yoko-key)))) | |
(defun sudoku-cross (key) | |
"十字のpearになるリストを返す。" | |
(-concat (sudoku-yoko key)(sudoku-tate key))) | |
(defun sudoku-peares (key) | |
"KEYのpearになるリストを返す。" | |
(let* ((peares (-concat (sudoku-units key) | |
(sudoku-cross key))) | |
(peares (remove-if (lambda(x)(equal x key)) peares))) | |
(delete-dups peares))) | |
(cl-defun sudoku-print (&optional (hash hash)) | |
"hashテーブルの中身をバッファに出力する。" | |
(let* ((w (loop for x in keys | |
collect (gethash x hash))) | |
(w (-partition 9 w)) | |
(w (loop for x in w | |
collect | |
(mapconcat 'concat (loop for y in (-partition 3 x) | |
collect | |
(mapconcat 'concat y " ")) "\t")))) | |
(insert (sudoku-table-convert (loop for x in w | |
concat (concat x "\n")))))) | |
(defun sudoku-value-overwrite (key value hash) | |
"hashのkeyの値をvalueにする。" | |
(let ((other-values (replace-regexp-in-string value "" (gethash key hash)))) | |
(not (loop for x in (cl-concatenate 'list other-values) | |
unless (sudoku-value-minus key (char-to-string x) hash) | |
return t)))) | |
(defun sudoku-value-minus (key value hash) | |
"hashのkeyの値からvalueを削除する。" | |
(let* ((w (gethash key hash)) | |
(rtn (cond ((not (search value w)) w) | |
((search value w)(let ((w2 (replace-regexp-in-string value "" w))) | |
(setf (gethash key hash) w2) | |
(case (length w2) | |
(0 nil) | |
(1 (not (loop for x in (sudoku-peares key) | |
unless (sudoku-value-minus x w2 hash) | |
return t))) | |
(t t))))))) | |
rtn | |
;; (when rtn | |
;; (not (loop for unit in (list (sudoku-tate key)(sudoku-yoko key)(sudoku-units key)) | |
;; unless (let ((w (loop for x in unit | |
;; when (search value (gethash x hash)) | |
;; collect x))) | |
;; (case (length w) | |
;; (0 (return nil)) | |
;; (1 (sudoku-value-overwrite (car w) value hash)) | |
;; (t t))) | |
;; return t))) | |
)) | |
(defun sudoku-set (string) | |
(loop for x in keys | |
for y in (sudoku-conv string) | |
when (string= (replace-regexp-in-string "\[1-9\]" "" y) "") | |
collect (sudoku-value-overwrite x y hash))) | |
(defun sudoku-conv (string) | |
(let* ((w (replace-regexp-in-string "\[^0-9.\]" " " string)) | |
(w (mapconcat 'char-to-string (cl-concatenate 'string w) " "))) | |
(split-string w))) | |
(defun sudoku-min (hash) | |
(loop for x in keys | |
unless (= (length (gethash x hash)) 1) | |
minimize (length (gethash x hash)))) | |
(defun sudoku-complete-p (hash) | |
(let ((w (loop for x in keys | |
when (= (length (gethash x hash)) 1) | |
count x))) | |
(when (= 81 w) | |
t))) | |
(defun sudoku-solve-run (hash) | |
(if (sudoku-complete-p hash) | |
hash | |
(let* ((min (sudoku-min hash)) | |
(key (loop for x in keys | |
when (= (length (gethash x hash)) min) | |
do (return x)))) | |
(loop for y in (cl-concatenate 'list (gethash key hash)) | |
do (let ((w (copy-hash-table hash)) | |
(x (char-to-string y))) | |
(when (sudoku-value-overwrite key x w) | |
(when-let (rtn (sudoku-solve-run w)) | |
(return rtn)))))))) | |
(defun sudoku-solve() | |
(when-let (w (sudoku-solve-run (copy-hash-table hash))) | |
(setq hash w)))) | |
(defun test () | |
(progn (sudoku-init) | |
(sudoku-set " | |
. . 5 |3 . . |. . . | |
8 . . |. . . |. 2 . | |
. 7 . |. 1 . |5 . . | |
------+------+------ | |
4 . . |. . 5 |3 . . | |
. 1 . |. 7 . |. . 6 | |
. . 3 |2 . . |. 8 . | |
------+------+------ | |
. 6 . |5 . . |. . 9 | |
. . 4 |. . . |. 3 . | |
. . . |. . 9 |7 . . | |
") | |
;; (sudoku-set " | |
;; 8 5 . |. . 2 |4 . . | |
;; 7 2 . |. . . |. . 9 | |
;; . . 4 |. . . |. . . | |
;; ------+------+------ | |
;; . . . |1 . 7 |. . 2 | |
;; 3 . 5 |. . . |9 . . | |
;; . 4 . |. . . |. . . | |
;; ------+------+------ | |
;; . . . |. 8 . |. 7 . | |
;; . 1 7 |. . . |. . . | |
;; . . . |. 3 6 |. 4 . | |
;; ") | |
(sudoku-solve) | |
(sudoku-print))) |