「 あらゆる数独パズルを解く」は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)"
ベンチマークもそこそこ。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; -*- 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))) |