构建一个Haskell程序来解决数独问题
发布时间:2023-12-10 12:23:25
数独是一种逻辑游戏,其中需要填写一个9x9的网格,使得每一行、每一列和每一个3x3的子网格都包含数字1-9,且每个数字只能出现一次。
要解决数独问题,我们可以使用递归和回溯算法来填写空白格子。
下面是一个用Haskell编写的解数独的程序:
import qualified Data.Vector as V
type Grid = V.Vector (V.Vector Int)
-- 检查一个数字是否可以填入指定的格子
isValid :: Grid -> (Int, Int) -> Int -> Bool
isValid grid (row, col) num =
let rowVals = grid V.! row
colVals = V.map (V.! col) grid
squareVals = [grid V.! i V.! j | i <- [row'..row'+2], j <- [col'..col'+2]]
row' = (row div 3) * 3
col' = (col div 3) * 3
in num notElem rowVals && num notElem colVals && num notElem squareVals
-- 在指定的格子中填入一个数字
updateGrid :: Grid -> (Int, Int) -> Int -> Grid
updateGrid grid (row, col) num =
grid V.// [(row, (grid V.! row) V.// [(col, num)])]
-- 找到下一个空白格子的坐标
nextEmpty :: Grid -> Maybe (Int, Int)
nextEmpty grid =
let indexes = [(i, j) | i <- [0..8], j <- [0..8]]
in foldl (\acc index@(row, col) -> if grid V.! row V.! col == 0 then Just index else acc) Nothing indexes
-- 解数独
solveSudoku :: Grid -> Maybe Grid
solveSudoku grid =
case nextEmpty grid of
Nothing -> Just grid -- 所有格子都已填满,解题成功
Just (row, col) ->
let possibleNums = filter (isValid grid (row, col)) [1..9]
in foldl (\acc num -> case acc of
Nothing -> solveSudoku (updateGrid grid (row, col) num)
Just _ -> acc) Nothing possibleNums
-- 打印数独
printGrid :: Grid -> IO ()
printGrid grid =
V.mapM_ (\row -> do
V.mapM_ (
um -> putStr $ show num ++ " ") row
putStrLn "") grid
-- 使用例子
main :: IO ()
main = do
let grid = V.fromList [ V.fromList [5, 3, 0, 0, 7, 0, 0, 0, 0]
, V.fromList [6, 0, 0, 1, 9, 5, 0, 0, 0]
, V.fromList [0, 9, 8, 0, 0, 0, 0, 6, 0]
, V.fromList [8, 0, 0, 0, 6, 0, 0, 0, 3]
, V.fromList [4, 0, 0, 8, 0, 3, 0, 0, 1]
, V.fromList [7, 0, 0, 0, 2, 0, 0, 0, 6]
, V.fromList [0, 6, 0, 0, 0, 0, 2, 8, 0]
, V.fromList [0, 0, 0, 4, 1, 9, 0, 0, 5]
, V.fromList [0, 0, 0, 0, 8, 0, 0, 7, 9]
]
putStrLn "原始数独:"
printGrid grid
putStrLn ""
case solveSudoku grid of
Nothing -> putStrLn "无解"
Just g -> do
putStrLn "解数独:"
printGrid g
这个程序中,首先定义了一个Grid类型,表示数独网格。然后定义了isValid函数,用于检查一个数字是否可以填入指定的格子;updateGrid函数,用于在指定的格子中填入一个数字;nextEmpty函数,用于找到下一个空白格子的坐标。
solveSudoku函数是主要的解数独函数,它首先检查是否还有空白格子,如果没有则返回成功的解,如果有,则尝试在当前空白格子中填入1-9的数字,然后递归调用自身继续解数独。
printGrid函数用于打印数独,按照行列格式将数独网格打印出来。
在main函数中,定义了一个初始的数独网格,并调用solveSudoku函数解题,最后打印出结果。
使用上述程序解题的输出如下:
原始数独: 5 3 0 0 7 0 0 0 0 6 0 0 1 9 5 0 0 0 0 9 8 0 0 0 0 6 0 8 0 0 0 6 0 0 0 3 4 0 0 8 0 3 0 0 1 7 0 0 0 2 0 0 0 6 0 6 0 0 0 0 2 8 0 0 0 0 4 1 9 0 0 5 0 0 0 0 8 0 0 7 9 解数独: 5 3 4 6 7 8 9 1 2 6 7 2 1 9 5 3 4 8 1 9 8 3 4 2 5 6 7 8 5 9 7 6 1 4 2 3 4 2 6 8 5 3 7 9 1 7 1 3 9 2 4 8 5 6 9 6 1 5 3 7 2 8 4 2 8 7 4 1 9 6 3 5 3 4 5 2 8 6 1 7 9
可以看到,程序成功解出了数独。
