【发布时间】:2016-02-16 02:45:45
【问题描述】:
我正在使用 SML/NJ 创建数独求解器。我已经准备好所有功能来实际操作输入数据(检查行的合法性,强制空格等),但我在回溯部分遇到了麻烦。
我遇到了this question,但我对如何在 SML 中实现它感到困惑。
请注意,棋盘是作为列表列表输入的,表示每行中的数字,0 表示未知点
[[0,0,0, 2,6,0, 7,0,1],
[6,8,0, 0,7,0, 0,9,0],
[1,9,0, 0,0,4, 5,0,0],
[8,2,0, 1,0,0, 0,4,0],
[0,0,4, 6,0,2, 9,0,0],
[0,5,0, 0,0,3, 0,2,8],
[0,0,9, 3,0,0, 0,7,4],
[0,4,0, 0,5,0, 0,3,6],
[7,0,3, 0,1,8, 0,0,0]]
这是我的(已编辑)solve 函数。
exception Sudoku;
fun solve board =
let fun solve' board k =
(* k is the row we are working on, so if it's 9, we SHOULD be done *)
if k = 9 then board else
let
(* get row k from the board *)
val row = (List.nth (board, k));
fun trySpot number col =
(* if number >= 10, raise an exception to backtrack *)
if number > (length row) then raise Sudoku
(* if col = 9, raise an exception to backtrack *)
else if col = 9 then raise Sudoku
(* if row[col] is not a zero, move to next col *)
else if not (List.nth(row, col) = 0) then trySpot number (col + 1)
(* row doesn't contain this num already *)
else if length (List.filter (fn x => x = number) row) = 0 then
let
(* build the new row and board and check if legal (this works fine) *)
val newRow = delete(col + 1, (insertAtPos row number col));
val newBoard = delete(k + 1, (insertAtPos board newRow k));
val isLegal = checkLegal newBoard;
in
(* if legal, keep solving with new board as base *)
if isLegal then
solve' (force newBoard) 0
handle Sudoku => solve' (force board) (k + 1)
(* not legal, try with next num *)
else trySpot (number + 1) col
end
(* row already has this number, skipping *)
else trySpot (number + 1) col
in
(* if board is complete and legal we're done *)
if completedBoard board andalso checkLegal board then board
(* if row has a zero then try a spot *)
else if (zeroInList row) then trySpot 1 0
(* otherwise move to next row *)
else solve' (force board) (k + 1)
end
in
(* initial solve *)
solve' (force board) 0
end;
对上面的示例数据调用solve会返回以下列表
[[4,3,5,2,6,9,7,8,1],
[6,8,2,5,7,1,4,9,3],
[1,9,7,8,3,4,5,6,2],
[8,2,6,1,9,5,3,4,7],
[3,1,4,6,8,2,9,0,0],
[0,5,0,0,0,3,0,2,8],
[0,0,9,3,0,0,0,7,4],
[2,4,0,0,5,0,0,3,6],
[7,0,3,0,1,8,0,0,0]]
现在这是部分正确的。根据我曾经检查过的在线数独求解器,前四行看起来完全正确,但在第 5 行就搞砸了。我猜是因为它无法一路回溯。
它“备份”的唯一地方就是这一行
handle Sudoku => solve' (force board) (k + 1)
这告诉它只尝试解决旧板(没有新号码),但这会阻止它回溯不止一步(我认为)。这怎么可能实现?
如果有人好奇想看完整代码,可以找here.
提前致谢!
【问题讨论】:
-
Hansen 和 Rischel 所著的“SML 编程简介”一书包含一个回溯算法示例,用于解决使用异常实现的 8 个皇后问题。我能够毫不费力地修改他们的代码以获得骑士之旅。它可能会给你一些想法(尽管现在我更可能使用选项而不是例外)。
-
谢谢推荐,我去看看
-
@JohnColeman,我看了一下他们的实现,并尝试修改我的求解函数以实现带异常的回溯。它在一定程度上有效,但我不确定如何让它进一步回到决策树中。有什么想法吗?
-
目前没有想法,我从来没有做过多少数独游戏(即使是手工也没有——我更喜欢填字游戏和密码)。我只是认为这本书会给你一些想法。如果我这周晚些时候有时间,我会考虑一下,但接下来的几天对我来说很忙。
标签: sml sudoku backtracking smlnj