我正在构建一个使用Try and Fail技术来解决任何问题的数独求解器。我的算法是:
1)更新(删除已作为同一行,列或方形中元素的最终值给出的任何可能值的方法)
2)获取具有最小可能值数的最小元素
3)开始解决假设第一个可能的值是最终值
4)将当前状态保存到堆栈中
5)尝试解决
5-a)如果已解决,请返回
5-b)如果没有解决并且使用无效的数独,则弹出前一个状态
6)对所有可能的vaues重复步骤3)(9)
7)重复步骤2)直到解决问题
这是我的代码
Stack<Element[][]> myStack= new Stack<>();
private Element[][] mySudoku;
public void solve(){
update();//remove all final values from all possible values for each element
if(isSudokuSolved(mySudoku)){
return;
}
//find a cell that is not confirmed and has the minimal candidates
int celli=-1,cellj=-1, p=10;
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
if(mySudoku[i][j].getValue()==0){
if(mySudoku[i][j].getPossibleValues().size()<p){
celli=i;
cellj=j;
p=mySudoku[i][j].getPossibleValues().size();
}
}
}
}
try {
for (int c = 0; c < mySudoku[celli][cellj].getPossibleValues().size() - 1; c++) {
//save state
Element[][] copy=deepCopy(mySudoku);//copy the current state
myStack.push(copy);
//apply candidate to cell
mySudoku[celli][cellj].setValue(mySudoku[celli][cellj].getPossibleValues().get(c));
update();//check is solved
if(checkValidInputSudoku(mySudoku)){
solve();
}else{
try {
mySudoku = myStack.pop();
} catch (EmptyStackException est) {
//do nothing
}
}
}
} catch (Exception e) {
}
//if we have reached here then we are at the last possible value for the candidates so confirm candidate in cell
if(celli!=-1 && cellj!=-1 && p!=10) {//Some problems happen here "out of Boundry -1 Error"
mySudoku[celli][cellj].setValue(mySudoku[celli][cellj].getPossibleValues().get(mySudoku[celli][cellj].getPossibleValues().size()-1));
}
}//end of solve method
我花了6个多小时试图找出问题所在。我检查了Update()方法,deepCopy()方法和checkValidInputSudoku()方法。它们都很好。提前谢谢
答案 0 :(得分:1)
我可以在您的代码中看到一个问题。你有一个循环,它正在锯掉它所在的分支:
for(int c = 0; c < mySudoku[celli][cellj].getPossibleValues().size() - 1; c++) {
...
mySudoku[celli][cellj].setValue(mySudoku[celli]cellj].getPossibleValues().get(c));
...
}
除此之外,您缺少其中一个值,它应该是for(c=0; c!=size; ++c)
,即不是size - 1
。此外,只调用一次getPossibleValues()会使这段代码更具可读性。最后,捕获和忽略堆栈下溢只是愚蠢,因为它隐藏了算法中的错误,据我所知。如果您不知道如何处理错误,请不要只是让它静音。因为java要求你抓住它,把它放在最可能的地方或至少中止或做某事,但不要忽略它!
还有一件事:您正在通过mySodoku
和myStack
递归并传递上下文数据。这完全没有递归点(或者至少是它通常使用的方式),因为函数调用堆栈是你需要的唯一堆栈。使用这些参数传递参数只会使事情变得更加复杂。相反,该函数应返回部分sodoku拼图并返回完全解决的拼图或null。使用比你现在使用的例外更容易区分,这是一个常规和预期的事情,并不是非常特殊。然后,在尝试不同的选择时,依次将单元格设置为值并递归,直到调用不返回null。如果没有任何选项返回解决方案,则清除单元格并自行返回null。
solve(sodoku):
if sodoku is solved:
return true
if sodoku is invalid:
return false
c = some empty cell
for v in 1...9:
// set to a value and recurse
c = v
if solve(sodoku):
// found a solution
return true
// no solution found, clear cell and return failure
c = null
return false
BTW:这种策略被称为“回溯”。使用具有最少可能值的单元格称为“修剪”,这允许您从搜索树中剪切整个分支。实际上,确定可能的值也有助于避免一些徒劳的尝试。