3.4 8パズルの解の存在
昨日の続き。また、同内容の問が3.4にあったりする。
15パズル - Wikipediaの「不可能な配置」の項目にあるように、「14 と 15 を入れ替えた状態」は解が存在しないパターンである。このように、任意の隣り合うピースを奇数回入れ替えると解なしとなり、一方、偶数回の入れ替えでは解が存在する。
というわけで、解の存在をチェックするには入れ替えの回数を数えて、奇数か偶数かで判定すればよい。この方法はパリティチェックの一種ではあるが、転倒数を数えるのとは少し違うようだ。
入れ替えの回数を数えるには、実際に並び替えて入れ替えた回数を調べればよい。今回はすべてのピースを動かして良いので、まともに解こうとすると後者が膨大になってしまうので、*goal*の最初から順に合わせていくことにする。この方法では最短手はでないが、入れ替えの偶奇には影響しないので今回は良しとする。入れ替えには前回作ったものがそのまま使える・・・といいたいところだが、中途半端な手抜きをしたので全然使えない^^;
(defun swap-piece (state piece move-to) (rotatef (cdr piece) (cdr (rassoc move-to state))) state) (defun move-piece (state piece path) (let ((mover (member (cdr piece) path :key 'car))) (if mover (swap-piece state piece (cdar mover))) mover)) (defun unmove-piece (state piece path) (swap-piece state piece (caar path))) (defun distance-sub (state piece goal path) (do ((depth 0) (cur-depth 0) (mover) (cur-mover) (len (length goal))) ((subsetp goal state :test 'equal) depth) (while (setq cur-mover (pop mover)) (unmove-piece state piece cur-mover) (let ((next-mover (move-piece state piece (cdr cur-mover)))) (if next-mover (return (push next-mover mover)) (decf cur-depth)))) (unless cur-mover (incf depth)) (while (> depth cur-depth) (push (move-piece state piece path) mover) (incf cur-depth)))) (defun distance (state goal path) (let ((copy-state (copy-tree state)) (phase 1) (step 0)) (distance-sub copy-state (car copy-state) (subseq goal 0 1) path) (dolist (a (cdr copy-state) step) (incf step (distance-sub copy-state a (subseq goal 0 (incf phase)) path))))) ;解ける状態を作る。 (progn (do-init 3) (if (oddp (distance *state* *goal* 3x3path)) (move-piece *state* (cadr *state*) (remove (cddr *state*) 3x3path :key 'cdr))) *state*)
もし解けない状態が生成されたら、生成をやりなおすのではなく、一度ピース同士を入れ替えれば良い。