n-女王問題

以前Haskellで書いたn-女王問題の解法

--------------------------------------------------------
-- queen.hs
-- n-女王問題 2007.04.25
--------------------------------------------------------

-- n-queen問題の回答を表示形式で列挙する。
printAns k = printP (map toPic (queenPuzzle k))

-- n-queen問題の回答を配列形式で出力する。
queenPuzzle k = removeDup (queen k)

-- n-queen問題の回答(重複を含む)を配列形式で出力する。
queen k = filter (checkQueen k) (makeElem k)





--------------------------------------------------------
-- 可能な配列を出力する。
-- 可能な配列とは、縦、横に重なりのない配列を意味する。
-- 斜めの重なりチェックはここでは行っていない。
-- 出力したい次数を指定すれば、任意の次元の可能な配列
-- の集合が得られる。
-- 重複はないが、回転による重複までは除去していない。
--------------------------------------------------------
makeElem k = map reverse (nextDim k 0 [[]])

nextDim :: Int -> Int -> [[Int]] -> [[Int]]
nextDim m k xs
  | m == k    = xs
  | otherwise = nextDim m (k+1) (concat [addDim m x | x <- xs])

addDim :: Int -> [Int] -> [[Int]]
addDim m xs = [x:xs| x <- [1 .. m], notElem x xs]

--------------------------------------------------------
-- 斜めの重なりをチェックする。
-- 与えられた配列に斜めの重なりが存在すれば False 、
-- 存在しなければ True を返す。
-- この関数はFilterに渡される。
--------------------------------------------------------
checkQueen :: Int -> [Int] -> Bool
checkQueen _ []   = True
checkQueen m (x:xs) = (checkSlant m (x+1) inc xs) &&
                      (checkSlant m (x-1) dec xs) &&
                      (checkQueen m xs)

inc :: Int -> Int
inc x = x+1
dec :: Int -> Int
dec x = x-1

checkSlant :: Int -> Int -> (Int -> Int) -> [Int] -> Bool
checkSlant m _ f []    = True
checkSlant m k f (x:xs)
  | k == x    = False
  | k == 0    = True
  | k > m     = True
  | otherwise = checkSlant m (f k) f xs


--------------------------------------------------------
-- 配列のリストから重複するものを取り除く。
-- リストの先頭要素と残りの要素の一致判定を行う。
-- この時、残りの要素は回転、反転を行い可能な重複パターンを全て確認する。
-- 確認の結果、全てのパターンで一致しない場合のみ重複でないと判定する。
-- 重複でないと判定された場合はリストに残す。
--------------------------------------------------------
removeDup :: [[Int]] -> [[Int]]
removeDup [] = []
removeDup (x:xs)
  | checkDupList x xs = x:removeDup xs
  | otherwise         = removeDup xs

checkDupList :: [Int] -> [[Int]] -> Bool
checkDupList x [] = True
checkDupList x xs = (checkDup x (head xs)) && (checkDupList x (tail xs))

--------------------------------------------------------
-- 2つの配列の一致判定を行う。
-- 可能な重複パターンを全てチェックする。
-- 一致していなければ True、一致していれば False
--------------------------------------------------------
checkDup :: [Int] -> [Int] -> Bool
checkDup x y = not ((same x (pattern0 y)) ||
                    (same x (pattern1 y)) ||
                    (same x (pattern2 y)) ||
                    (same x (pattern3 y)) ||
                    (same x (pattern4 y)) ||
                    (same x (pattern5 y)) ||
                    (same x (pattern6 y)) ||
                    (same x (pattern7 y)))

pattern0 x = x
pattern1 x = toElem (reverse (toPic x))
pattern2 x = toElem (map reverse (toPic x))
pattern3 x = toElem (reverse (map reverse (toPic x)))
pattern4 x = toElem (rotate (toPic x))
pattern5 x = toElem (reverse (rotate (toPic x)))
pattern6 x = toElem (map reverse (rotate (toPic x)))
pattern7 x = toElem (reverse (map reverse (rotate (toPic x))))

--------------------------------------------------------
-- 2つの配列が同一かどうか調べる。
-- 同一であれば True、異なっていれば False
--------------------------------------------------------
same :: [Int] -> [Int] -> Bool
same [] [] = True
same (x:xs) (y:ys)
  | x /= y = False
  | otherwise = same xs ys 



--------------------------------------------------------
-- 配列を表示形式に変換する。
--------------------------------------------------------
toPic xs = map reverse (convert xs)

convert :: [Int] -> [[Int]]
convert xs = map (convertPic (length xs) 1 []) xs

convertPic :: Int -> Int -> [Int] -> Int -> [Int]
convertPic m n xs k
  | m < n     = xs
  | n == k    = convertPic m (n+1) (1:xs) k
  | otherwise = convertPic m (n+1) (0:xs) k

--------------------------------------------------------
-- 表示形式を配列に変換する。
--------------------------------------------------------
toElem pic = map (index 1) pic

index :: Int -> [Int] -> Int
index _ [] = 0
index k (x:xs)
  | x == 1    = k
  | otherwise = index (k+1) xs

--------------------------------------------------------
-- 表示形式を90度回転させる。
--------------------------------------------------------
rotate :: [[Int]] -> [[Int]]
rotate x = rotate_nth 0 (length x) x

rotate_nth :: Int -> Int -> [[Int]] -> [[Int]]
rotate_nth n max x
  | n == max  = []
  | otherwise = (take_nth n x) : (rotate_nth (n+1) max x)

take_nth :: Int -> [[Int]] -> [Int]
take_nth n x = map (nth_element n) x

nth_element :: Int -> [Int] -> Int
nth_element n x = x!!n


--------------------------------------------------------
-- 出力関数
--------------------------------------------------------

-- 1つの表示形式を出力する。
printPicture :: [[Int]] -> IO ()
printPicture pic
             = do putStr (show (head pic))
                  putStr "\n"
                  if (tail pic) == [] then putStr "\n" else printPicture (tail pic)


-- 表示形式のリストを出力する。
printP :: [[[Int]]] -> IO ()
printP pp = do printPicture (head pp)
--               putStr "\n"
               if (tail pp) == [] then putStr "\n" else printP (tail pp)


これで解いたn=8の結果

[0,0,0,0,0,1,0,0]
[0,0,1,0,0,0,0,0]
[0,0,0,0,0,0,1,0]
[0,0,0,1,0,0,0,0]
[1,0,0,0,0,0,0,0]
[0,0,0,0,0,0,0,1]
[0,1,0,0,0,0,0,0]
[0,0,0,0,1,0,0,0]

[0,0,0,0,0,1,0,0]
[0,0,0,1,0,0,0,0]
[1,0,0,0,0,0,0,0]
[0,0,0,0,1,0,0,0]
[0,0,0,0,0,0,0,1]
[0,1,0,0,0,0,0,0]
[0,0,0,0,0,0,1,0]
[0,0,1,0,0,0,0,0]

[0,0,0,0,0,1,0,0]
[0,0,0,1,0,0,0,0]
[0,0,0,0,0,0,1,0]
[1,0,0,0,0,0,0,0]
[0,0,0,0,0,0,0,1]
[0,1,0,0,0,0,0,0]
[0,0,0,0,1,0,0,0]
[0,0,1,0,0,0,0,0]

[0,0,0,0,0,0,1,0]
[0,1,0,0,0,0,0,0]
[0,0,0,1,0,0,0,0]
[1,0,0,0,0,0,0,0]
[0,0,0,0,0,0,0,1]
[0,0,0,0,1,0,0,0]
[0,0,1,0,0,0,0,0]
[0,0,0,0,0,1,0,0]

[0,0,0,0,0,0,1,0]
[0,1,0,0,0,0,0,0]
[0,0,0,0,0,1,0,0]
[0,0,1,0,0,0,0,0]
[1,0,0,0,0,0,0,0]
[0,0,0,1,0,0,0,0]
[0,0,0,0,0,0,0,1]
[0,0,0,0,1,0,0,0]

[0,0,0,0,0,0,1,0]
[0,0,1,0,0,0,0,0]
[1,0,0,0,0,0,0,0]
[0,0,0,0,0,1,0,0]
[0,0,0,0,0,0,0,1]
[0,0,0,0,1,0,0,0]
[0,1,0,0,0,0,0,0]
[0,0,0,1,0,0,0,0]

[0,0,0,0,0,0,1,0]
[0,0,1,0,0,0,0,0]
[0,0,0,0,0,0,0,1]
[0,1,0,0,0,0,0,0]
[0,0,0,0,1,0,0,0]
[1,0,0,0,0,0,0,0]
[0,0,0,0,0,1,0,0]
[0,0,0,1,0,0,0,0]

[0,0,0,0,0,0,1,0]
[0,0,0,1,0,0,0,0]
[0,1,0,0,0,0,0,0]
[0,0,0,0,1,0,0,0]
[0,0,0,0,0,0,0,1]
[1,0,0,0,0,0,0,0]
[0,0,1,0,0,0,0,0]
[0,0,0,0,0,1,0,0]

[0,0,0,0,0,0,1,0]
[0,0,0,1,0,0,0,0]
[0,1,0,0,0,0,0,0]
[0,0,0,0,0,0,0,1]
[0,0,0,0,0,1,0,0]
[1,0,0,0,0,0,0,0]
[0,0,1,0,0,0,0,0]
[0,0,0,0,1,0,0,0]

[0,0,0,0,0,0,1,0]
[0,0,0,0,1,0,0,0]
[0,0,1,0,0,0,0,0]
[1,0,0,0,0,0,0,0]
[0,0,0,0,0,1,0,0]
[0,0,0,0,0,0,0,1]
[0,1,0,0,0,0,0,0]
[0,0,0,1,0,0,0,0]

[0,0,0,0,0,0,0,1]
[0,0,1,0,0,0,0,0]
[1,0,0,0,0,0,0,0]
[0,0,0,0,0,1,0,0]
[0,1,0,0,0,0,0,0]
[0,0,0,0,1,0,0,0]
[0,0,0,0,0,0,1,0]
[0,0,0,1,0,0,0,0]

[0,0,0,0,0,0,0,1]
[0,0,0,1,0,0,0,0]
[1,0,0,0,0,0,0,0]
[0,0,1,0,0,0,0,0]
[0,0,0,0,0,1,0,0]
[0,1,0,0,0,0,0,0]
[0,0,0,0,0,0,1,0]
[0,0,0,0,1,0,0,0]