Problem 96 (Haskellで数独を解く)

http://projecteuler.net/index.php?section=problems&id=96

数独を解く問題。やっと速く動くようになった。
まったく、バグが大変だった。

import Data.List
import Data.Char
import Data.Ord
import Data.Array.IArray
import Control.Monad

type Sudoku = Array (Int,Int) [Int]

sieve :: Sudoku -> ((Int,Int),Int) -> Sudoku
sieve a ((i,j),n) = accum (flip delete) a [(ix,n)|ix<-r++c++b,ix/=(i,j)] // [((i,j),[n])]
  where r = zip [0..8] $repeat j
        c = zip (repeat i) [0..8]
        b = [(i`div`3*3+k,j`div`3*3+l)|(k,l)<-range((0,0),(2,2))]

unfilled :: Sudoku -> [((Int,Int),[Int])]
unfilled = sortBy(comparing $ length.snd).map delZero.filter((/=1).length.snd).assocs
    where delZero (x,ys) = (x,delete 0 ys)


solve ::[Sudoku] -> Sudoku
solve (a:as) | all finish $elems  a  = a -- finish
             | any unfeasible $ elems a = solve as -- unfeasible
             | unique /= [] = solve$fillUni a : as -- fill unique numbers
             | otherwise = solve$guess a ++as -- guess number 
    where finish x = [0]/=x&&length x == 1
          unfeasible x = [0]==x||[]==x
          fillUni a = foldl sieve a [(ix,n)|(ix,[n])<-unique]
          guess a =[sieve a (ix,n) |(ix,n)<-unpack.head$nonUni]
          unpack (x,ys) = zip (repeat x) $  ys
          (unique,nonUni) = break ((>1).length.snd) . unfilled  $ a

ini ::[Int] -> Sudoku
ini xs = foldl sieve a0 $ [(ix,y)|(ix,y)<-zip (range((0,0),(8,8))) xs,y/=0]
    where a0 = listArray ((0,0),(8,8)) $replicate 81 [0..9]

getGrids = map (concatMap $ map digitToInt).take 50.map (take 9.tail).iterate (drop 10).lines

main = do
 f <- liftM getGrids . readFile  $ "sudoku.txt"
 xs <- mapM (return.map intToDigit.concat.take 3.elems.solve.return.ini) f
 print.sum.map read$xs

とりあえず方針は
深さ優先探索
取りうる数の範囲が狭い場所から埋めていく
途中で、一意に定まる箇所が複数出てきたら、
全部一緒に埋めて実行可能性のチェック
という感じ。