所以我正在编写一个数独求解器,并且必须创建求解函数。 给出:
solve :: Sudoku -> [Maybe Sudoku]
其中数独是[[Maybe Int]]
。
这是通过蛮力来解决的,所以我递归检查数据是否可以解决,是否已满但是打破约束(例如行/列/块中的重复数字),否则插入1-9递归地在第一个找到的空白点,直到它工作或直到我知道它将永远不会工作。
问题出现的时候,我发现第一个空白区域接受1作为新输入,但后来意识到这不起作用,然后我必须返回并将1更改为2,或者无论哪个有效接下来再试一次。我该怎么做?这是我目前解决的代码:
solve :: Sudoku -> [Maybe Sudoku]
solve sud
| isSudoku sud && isSolved sud && isOkay sud = [Just sud]
| isSudoku sud && isSolved sud && not (isOkay sud) = [Nothing]
| isSudoku sud && not (isSolved sud) = solve (helper sud (blank sud) False 1)
helper :: Sudoku -> Pos -> Bool -> Int -> Sudoku
helper sud pos check n
| n > 9 || n < 1 || check = sud
| n > 0 && n < 10 && not check =
do
let newSud = (update sud pos (Just n))
helper newSud pos (isOkay newSud) (n+1)
关于如何解决这个问题的任何意见?
编辑:数独实现为:
data Sudoku = Sudoku [[Maybe Int]]
deriving ( Eq )
对于我到目前为止的反馈,上面的代码已经解决了sudokus。问题在于,当有足够的空白点时,某个地点可以接受多个数字,而不是只使用一个。假设一个空白点适用于数字5和8,但8是正确答案,5表示无法解决。然后我必须回去改变它并尝试再次解决所有下一个空白。
答案 0 :(得分:1)
这真的很广泛,但基本上对于这种算法,list-monad是你的朋友;) - 如果你只是代表你无法找到解决方案[] <你也不需要那里的Maybe / p>
以下是伪代码:
solve :: Sudoku -> [Sudoku]
solve sud
| isSudoku sud && isSolved sud && isOkay sud = [sud]
| isSudoku sud && isSolved sud && not (isOkay sud) = []
| isSudoku sud && not (isSolved sud) = do
nr <- [1..9]
let sud' = sud `updateNextUnsetCellWith` nr
solve sud'
当然你必须首先编写updateNextUnsetCellWith
函数 - 它应该只将nr
设置为第一个未设置的单元格并返回更新的状态,当然它假设其他两个案例将触发没有未设置的细胞。
请注意这个强力变种会让你感到疯狂,因为很长一段时间会产生合理问题的结果。
答案 1 :(得分:0)
我还没有检查过您的代码,但总的来说,您必须采取行动,好像有多个解决方案并且您尝试找到所有这些解决方案。这意味着您可以随时迭代所有可能的选择,其中选项为(Cell, Integer)
。
所以你输入第一个选项,它会给你一个更完整的数独,然后尝试以同样的方式解决新的数独。这会导致解决数独或在某些时候你在sudoko完成之前用完选择。所以在任何时候你都会收到一个已解决的数据,或者表示没有解决方案。在后一种情况下,你尝试下一个选择,直到数据被解决或者你自己用完了选择,并且你返回一个迹象表明这条路径没有解决方案。
作为旁注:当我尝试这种方法时,我不得不意识到暴力太慢了。当您订购选择时,您可以获得可接受的性能,以便首先尝试选择次数最少的单元格。
另一方面注意:为了测试这个,您可以使用已解决的数据并删除一个数字并查看是否找到了解决方案。然后你删除两个号码等。