数独回溯 无效数独

Posted

技术标签:

【中文标题】数独回溯 无效数独【英文标题】:Sudoku Backtracking Non valid Sudoku 【发布时间】:2013-02-27 22:47:16 【问题描述】:

我创建了一个数独回溯求解器,它工作得很好, 但是现在如果数独由于无效而无法解决,我想给出一个错误 例如,如果给出这个数独:

http://img5.imageshack.us/img5/2241/sudokugq.jpg

如果我的解决方法无法解决,我该怎么做才能让我的解决方法出错? 我总是以零结尾或陷入循环。

    public void solve( int row, int col ) throws Exception
   
      // Throw an exception to stop the process if the puzzle is solved
      if( row > 8 )
        //Gelöst
      
      // If the cell is not empty, continue with the next cell
      if( sudo[row][col] != 0 )
         next( row, col ) ;
        
      else
      
         // Find a valid number for the empty cell
         for( int num = 1; num < 10; num++ )
         
            if( checkHorizontal(row,num) == false && checkVertikal(col,num)== false&& checkBox(row,col,num)== false )
            
               sudo[row][col] = num ;


               // Delegate work on the next cell to a resudosive call
               next( row, col ) ;
            
         

         // No valid number was found, clean up and return to caller
         sudo[row][col] = 0 ;
      

   

   //Ruft solve für das nächste Feld auf
   public void next( int row, int col ) throws Exception
   
      if( col < 8 )
         solve( row, col + 1 ) ;
      else
         solve( row + 1, 0 ) ;
   

【问题讨论】:

我在这里发现了一个丑陋的设计:solve 依赖于nextnext 依赖于solve 这有什么问题? @LuiggiMendoza - 我见过比这更复杂的递归解决方案。尽管它很复杂,但我认为它也是解决方案的真正实现。此外 - next 只是选择下一个要尝试的坐标。 是的,但这并不能解决我的问题。我的问题是:如果有一个无效的数独(如图所示),我该怎么做才能给出错误并退出循环? 【参考方案1】:

当然,当您点击sudo[row][col] = 0 ; 代码时,您刚刚尝试完正方形中的每个值,但没有找到任何解决方案。当然,这就是您决定放弃无解决方案状态的关键。

进一步考虑 - 我怀疑我几乎是对的。如果您点击此代码并且这是您正在尝试的第一个单元格(即您找到的第一个空单元格),那么这就是您的失败时刻。

顺便说一句 - 我不确定使用 Exception 来指示您在评论中建议的解决方案是否是个好主意。


这现在正确地拒绝了您发布的板 - 请注意,不仅第一行有两个“3”,而且第一列还有两个“5”。这段代码现在似乎对我有用。也许你可以在一个不溶的上试一试。

public class Test 

    static final int S = 9;
    int[][] sudo;

    public Test() 
        // Just leave it empty.
        sudo = new int[S][S];
    

    public Test(int[][] sudo) 
        this.sudo = sudo;
    

    // This number should not appear anywhere in the row.
    public boolean checkHorizontal(int row, int num) 
        for (int i = 0; i < S; i++) 
            if (sudo[row][i] == num) 
                return false;
            
        
        return true;
    

    // This number should not appear anywhere in the col.
    public boolean checkVertical(int col, int num) 
        for (int i = 0; i < S; i++) 
            if (sudo[i][col] == num) 
                return false;
            
        
        return true;
    

    // This number should not appear anywhere in the box.
    private boolean checkBox(int row, int col, int num) 
        // Round down to nearest 3.
        int r = (row / 3) * 3;
        int c = (col / 3) * 3;
        for (int i = r; i < r + 3; i++) 
            for (int j = c; j < c + 3; j++) 
                if (sudo[i][j] == num) 
                    return false;
                
            
        
        return true;
    

    boolean check(int row, int col, int num) 
        // All checks must pass.
        return checkHorizontal(row, num)
                && checkVertical(col, num)
                && checkBox(row, col, num);
    

    public boolean solve(int row, int col) 
        // Got to the end?
        if (row >= S) 
            //Gelöst
            return true;
        
        // If the cell is empty
        if (sudo[row][col] == 0) 
            // Find a valid number for the empty cell
            for (int num = 1; num < 10; num++) 
                // Can this number be put there?
                if (check(row, col, num)) 
                    // Yes!
                    sudo[row][col] = num;
                    // Try that.
                    if (next(row, col)) 
                        return true;
                    
                
            
            // Clean up.
            sudo[row][col] = 0;
         else 
            // Move on.
            if (next(row, col)) 
                return true;
            
        
        // Failed to find a solution.
        return false;
    

    //Ruft solve für das nächste Feld auf
    public boolean next(int row, int col) 
        if (col < S - 1) 
            // Step right.
            return solve(row, col + 1);
         else 
            // Step down.
            return solve(row + 1, 0);
        
    

    public boolean boardOk() 
        // Initial test to ensure it is a valid array.
        // Must have that many rows.
        boolean ok = sudo.length == S;
        for (int row = 0; ok && row < S; row++) 
            // Each row must be that long.
            ok &= sudo[row].length == S;
            for (int col = 0; ok && col < S; col++) 
                // Each filled square must be valid.
                if (sudo[row][col] != 0) 
                    int num = sudo[row][col];
                    // Remove it.
                    sudo[row][col] = 0;
                    // Check it can be added.
                    ok &= check(row, col, num);
                    // Put it back.
                    sudo[row][col] = num;
                
            
        
        return ok;
    

    void solve() 

        if (boardOk()) 
            boolean solved = solve(0, 0);
            if (solved) 
                System.out.println("Solved!");
                print();
            
         else 
            System.out.println("Insoluble!");
            print();
        
    

    public void print() 
        for (int i = 0; i < sudo.length; i++) 
            System.out.println(Arrays.toString(sudo[i]));
        
    

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) 
        new Test().solve();
        int[][] test = 
            0, 0, 6, 5, 8, 3, 3, 2, 7,
            0, 0, 0, 0, 9, 0, 0, 5, 0,
            5, 8, 0, 0, 0, 0, 0, 0, 3,
            0, 3, 1, 0, 4, 0, 5, 0, 0,
            0, 0, 0, 9, 2, 0, 3, 0, 6,
            0, 0, 0, 0, 0, 0, 0, 0, 1,
            3, 4, 2, 0, 0, 6, 9, 1, 5,
            5, 0, 5, 4, 0, 9, 0, 3, 2,
            0, 1, 0, 0, 0, 0, 0, 0, 4,;
        new Test(test).solve();
    

【讨论】:

不,sudo[row][col] = 0;只是表示它倒退了一步 我不明白您所说的“这是您尝试的第一个单元格”的意思,您能解释一下吗?对不起,如果这听起来很愚蠢,英语不是我的母语 不傻 - 给我几分钟 - 我正在尝试一些东西。 :) 你在数独板上错过了一个 3,一个 3 没问题,因为它是有效的。但是一排有 2 个 3 是无效的,所以它不能工作 已编辑代码 - 请重试 - 实际上您发布的电路板存在两个问题。第一列还有两个“5”。

以上是关于数独回溯 无效数独的主要内容,如果未能解决你的问题,请参考以下文章

数独回溯算法

保存结果数独回溯

求解数独的回溯算法

如何通过回溯和递归解决数独?

优化回溯算法求解数独

Gui 可视化递归回溯数独