Я работаю над созданием Soloku Solvo с SML/NJ. У меня есть все функции для фактического манипулирования входными данными (проверка законности строк, форсирование пустых пространств и т. Д.), Но у меня возникают проблемы с частью обратной трассировки.Судоку Solver в SML с обратным отсчетом
Я столкнулся с 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]]
Теперь это отчасти верно. Первые четыре строки выглядят полностью корректными на основе онлайн-решения sudoku, который я использовал для проверки, но он перепутался с 5-й строкой. Я полагаю, это потому, что он не может полностью отступить.
Единственное место, где она «поддерживает» на этой линии
handle Sudoku => solve' (force board) (k + 1)
Это говорит, что это просто попытаться решить старую доску (без нового номера), но это препятствует его от возвратов больше, чем на один шаг (Я думаю). Как это можно сделать?
Если кому-то интересно, чтобы увидеть полный код, вы можете найти его here.
Спасибо заранее!
В книге «Введение в программирование с использованием SML» Хансена и Ришель есть пример алгоритма обратного отслеживания для решения проблемы 8 ферзей, реализованной с использованием исключений. Я смог изменить свой код, чтобы получить рыцарские туры без особых трудностей. Это может дать вам некоторые идеи (хотя в наши дни я бы скорее использовал варианты, а не исключения). –
Спасибо за рекомендацию, я посмотрю –
@JohnColeman, я взглянул на их реализацию и попытался изменить мою функцию решения, чтобы реализовать откат с исключениями. Это работает до некоторой степени, но я не уверен, как заставить его вернуться в дерево решений. Есть идеи? –