私は学校のプロジェクトのためにキラー数独ソルバーを開発しています。私は、可能な限り最も教育的であるために彼らが何をしているのかを示す10の人間の戦略をプログラムしました。
今は難しいキラー数独を解くことができますが、私の先生は、可能なすべてのグリッドを解くためにバックトラッキングを使用することを提案しました。
私は1週間、バックトラックに関する一般的なリソースまたは数独に基づくリソースを検索しようとしています...実際、Killer Sudokuでは、ゾーンと以前の内容に依存せずにセルに解決策があるとは言えません。書かれたセルは、あらゆる場所のゾーンと可能性を意味します。
私がやりたいのは、数値がより効率的で唯一の解決策を返すことを提案するたびに、私のすべての戦略を適用することです。
しかし、戦略を適用するには、グリッドのコピーが必要ですよね?したがって、いくつかのメソッドを使用してオブジェクトを完全に複製し、別のメソッドを使用して戦略を適用できます。
この関数の構造は何でしょうか?
PS:私はいくつかのコードを書くことを数回試みましたが、返される解決策が正しくないたびに。 (グリッドの有効性を簡単に確認できます)
それはこのようなものでした:
def bt(sudoku)
if sudoku.valid? then
true
else
sudoku2 = sudoku.clone
sudoku2.first_cell_not_solved.possibilities.each do |p|
sudoku2.first_cell_not_solved.solution = p
end
sudoku2.apply_all_strategies
if sudoku.completed?
return sudoku.valid?
else
bt(sudoku2)
end
end
end
あなたがいるところsudoku2.first_cell_not_solved.set_first_possibility_as_solution
、実際には、解決されていない最初のセルのすべての可能性をループし、次に解決されていない2番目のセルのすべての可能性をループする必要があります。その可能性を選択するたびに、戦略を適用して解決策が得られるかどうかを確認します。そうでない場合は、その可能性を元に戻し(つまり、バックトラック)、次の可能性を選択します。最初の可能性を選択するだけで、サンプルコードは実際にはバックトラックを実行しません。
この種のブルートフォースアルゴリズムは、非常に長い時間がかかる可能性があります。秘訣は、最初に行く適切なセルを選択することです。たとえば、可能性が2つしかないセルを選択すると、大きく開いているセルを選択するよりも早く結果が得られる可能性があります。また、同じサブパズルを何度も解決する可能性が高いため、通常、何らかのメモ化が非常に役立ちます。
編集:このようなもの(Ruby間違い、私は言語がわかりません)を許してください):
def bt(sudoku)
if sudoku.completed? then
return sudoku.valid?
else
sudoku.first_cell_not_solved.possibilities.each do |p|
sudoku2 = sudoku.clone
sudoku2.first_cell_not_solved.solution = p
sudoku2.apply_all_strategies
if bt(sudoku2)
sudoku = sudoku2
true
end
end
end
false
end
特定の数独パズルを解くよりも、徹底的なバックトラックプログラムを作成する方が実際には簡単です。アルゴリズムは非常に単純です。グリッドのクローンを作成する必要はありません。
value[i] = 0 for i in 0..totalCells-1;
int currentCell = 0;
while (currentCell != totalCells) {
++value[currentCell];
if (value[currentCell] > 9) {
value[currentCell] = 0; // erase
--currentCell; // backtrack
if (currentCell < 0) throw new UnsolvableSudokuException();
} else if (!anyConstraintViolated) {
++currentCell; // advance to next cell
}
}
これは長時間実行されると思われるかもしれませんが、Perlのような遅い言語でも、このアルゴリズムは通常の数独を数秒で解決します。
クローン作成の代わりに、 memento パターンを使用することもできます。
def bt(sudoku)
if !sudoku.valid?
throw Exception()
if sudoku.completed? then
true
else
memento = sudoku.dump
sudoku.first_cell_not_solved.possibilities.each do |p|
sudoku.first_cell_not_solved.solution = p
sudoku.apply_all_strategies
if sudoku.valid? and bt(sudoku)
return true
sudoku.restore(memento)
end
false
end
end