逻辑求解算法(Java数独)

11
我在解决逻辑求解算法方面遇到了问题。它可以很好地解决具有大量提示的难题,但是对于少于45个线索的难题存在问题。
这是用于解决的算法。Immutable是一个布尔值,用于确定该值是否可以更改。cell [row] [col]。possibleValues是SudokuCell类中LinkedList的一部分,其中存储该格元素可能的值。 grid.sGrid是谜题的主int [] []数组。 removeFromCells()是从网格的行、列和象限中删除值的方法。该代码在下面提供。
第二个for循环仅用于检查单个解决方案。我决定避免使用递归,因为我真的无法理解它。这种方法现在似乎足够好用。
public boolean solve(){

    for(int i = 0; i < 81; i++){
        for(int row = 0; row < 9; row++){
            for(int col = 0; col < 9; col++){
                if(!immutable[row][col]){
                    if(cell[row][col].getSize() == 1){
                        int value = cell[row][col].possibleValues.get(0);
                        grid.sGrid[row][col] = value;
                        immutable[row][col] = true;
                        removeFromCells(row, col, value);
                    }
                }
            }
        }
    }


    int i = 0;
    for(int row = 0; row < 9; row++){
        for(int col = 0; col < 9; col++){
            if(grid.sGrid[row][col] == 0){
                i++;
            }
        }
    }

    if(i != 0){
        return false;
    } else{
        return true;
    }
}

这是removeFromCells()的代码。

我认为大部分代码都很容易理解。第一个for循环从(x, y)所在的行和列中删除值,第二个循环从象限中删除值。

public void removeFromCells(int x, int y, int value){

    /*
     * First thing to do, find the quadrant where cell[x][y] belong.
     */

    int topLeftCornerRow = 3 * (x / 3) ;
    int topLeftCornerCol = 3 * (y / 3) ;

    /*
     * Remove the values from each row and column including the one
     * where the original value to be removed is.
     */
    for(int i = 0; i < 9; i++){
        cell[i][y].removeValue(value);
        cell[x][i].removeValue(value);
    }


    for(int row = 0; row < 3; row++){
        for(int col = 0; col < 3; col++){
            cell[topLeftCornerRow + row][topLeftCornerCol + col].removeValue(value);
        }
    }
}

可能有问题的地方是可能值的构建。这是我用于此的方法:

第一个for循环创建新的SudokuCells以避免可怕的空指针异常。

sGrid中的任何null值都表示为0,因此for循环跳过它们。

SudokuBoard的构造函数调用此方法,所以我知道它正在被调用。

public void constructBoard(){

    for(int row = 0; row < 9; row++){
        for(int col = 0; col < 9; col++){
            cell[row][col] = new SudokuCell();
        }
    }

    immutable = new boolean[9][9];

    for(int row = 0; row < 9; row++){
        for(int col = 0; col < 9; col++){
            immutable[row][col] = false;
            if(grid.sGrid[row][col] != 0){
                removeFromCells(row, col, grid.sGrid[row][col]);
                immutable[row][col] = true;
            }
        }
    }
}
我会将整个文件发布出来,但里面有很多不必要的方法。我已经张贴了我认为导致问题的部分。

你得到了什么样的行为,你希望得到什么样的行为? - StriplingWarrior
1
你知道 if(i != 0){ return false; } else{ return true; } 等价于 return (i != 0); 吗?(第一个代码块) - user unknown
2个回答

6
您似乎目前只构建了一个简单的基于约束的求解器。为了解决提示较少的谜题,您需要一个完整的回溯算法。有些情况下,您确实无法在没有回溯的情况下解决问题。
或者,您可以尝试实现Knuth的算法(Dancing Links)来解决这类问题。它比回溯算法更复杂,但运行效果要好得多 :)。请参见此处:http://en.wikipedia.org/wiki/Dancing_Links 它也是一个更一般的问题的算法,并且已成功应用于解决数独问题。
在维基百科上,有一篇文章详细介绍了使用约束编程可以解决哪些问题:http://4c.ucc.ie/~hsimonis/sudoku.pdf(从这里找到:http://en.wikipedia.org/wiki/Sudoku_algorithms)。表4非常有趣 :)。

我编写了一个暴力算法,可以解决任何问题。我写这篇文章的原因是我想要一个可以测试单个解决方案的算法。如果我错了,请纠正我,但是这种方法不是逻辑求解器中广泛使用的方法吗? - SkylineAddict
2
我发现使用带有前向检查的回溯比跳跃链接要快得多,并且实现起来相当容易。 - Fred Foo
有没有更好的方法来检查某个方格可能的值?我意识到,正如Toader和Stripling所说,我的方法在确定一个方格的可能值方面是正确的,但它无法解决更难的谜题。 - SkylineAddict
@skyline,请查看我在帖子中提供的PDF链接。对我来说,它非常全面,应该能指向所有已知的通过逻辑解决数独的更好算法。 - Mihai Toader
我想补充一下,跳舞链算法在技术上属于回溯算法。巧妙的数据结构是它唯一的优势。否则,它只是一个强化版的暴力搜索算法。 - st0le
显示剩余5条评论

2
我使用了许多这样的规则来开发数独求解器。但是,对于非常难的数独题目,我总是不得不使用回溯算法。根据维基百科的说法,有些数独只能通过使用规则来解决。
我实现了6条规则:
  1. 没有其他值被允许出现。
  2. 某个值在同一区域的其他方格中都不允许出现。
  3. 某个值在同一行或列的其他方格中都不允许出现。
  4. 某个值仅允许在一个区域内的一行或一列中出现,因此可以从该行或列中在其他区域中消除该值。
  5. 明显对偶
  6. 双线摒除法
我在这两篇博客文章中描述了整个算法并提供了代码(最初版本只使用了前4条规则): http://www.byteauthor.com/2010/08/sudoku-solver/ http://www.byteauthor.com/2010/08/sudoku-solver-update/ 附注:我的算法针对性能进行了优化,因此它可以自动平衡回溯和这些规则,即使有时不需要猜测。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接