数独求解器使用尝试和失败技术重复叠加

时间:2013-04-30 19:46:37

标签: java algorithm recursion stack sudoku

我正在构建一个使用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()方法。它们都很好。提前谢谢

1 个答案:

答案 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要求你抓住它,把它放在最可能的地方或至少中止或做某事,但不要忽略它!

还有一件事:您正在通过mySodokumyStack递归并传递上下文数据。这完全没有递归点(或者至少是它通常使用的方式),因为函数调用堆栈是你需要的唯一堆栈。使用这些参数传递参数只会使事情变得更加复杂。相反,该函数应返回部分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:这种策略被称为“回溯”。使用具有最少可能值的单元格称为“修剪”,这允许您从搜索树中剪切整个分支。实际上,确定可能的值也有助于避免一些徒劳的尝试。