我的循环永无止境

My loop never ends

我的程序有问题,它应该解决数独并在完成后打印,但问题是,程序看起来永远不会结束,在打印解决的数独后出现错误。

这是程序正在执行的循环: 它也不会打印字段,如果我在开始时删除 System.out.print(f+"\n"); 它只会打印错误。 代码:

        public class Sudoku {


  public static void main(String[] args) throws SolvedException {
    Field field = new Field();
    field.fromFile("test1.txt");
    SudokuSolver solver = new SudokuSolver();

    solve(field, 0, 0, solver);

    System.out.println(field);

  }




public static void solve(Field f, int i, int j, SudokuSolver solver) {

    System.out.print(f+"\n");

    if ( j >= Field.SIZE) {

        //we are done (return true now!)
        solver.done=true;
        return;

    } 

    if (f.isEmpty(i, j)) {

        for (int val = 1; val <=9; val++) {

            if (f.tryValue(val, i, j)){

                if (j>=Field.SIZE-1){

                    solve (f, i+1, 0, solver);

                    if ( solver.done ) {

                        // This halts the loop here:
                        return;
                    }

                    f.clear(i, j);

                } else {

                    solve(f,i,j+1, solver);

                    if ( solver.done ) {

                        // This halts the loop here:
                        return;
                    }

                    f.clear(i, j);

                }

            }
        }

    } else if (j>=Field.SIZE-1) {

        solve(f,i+1,0, solver);

    } else {

        solve(f,i,j+1, solver);

    }
}
}

数独求解器

public class SudokuSolver{

    /* Set true when the solve is done */
    public boolean done;

}

错误:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 9
    at Field.isEmpty(Field.java:101)
    at Sudoku.solve(Sudoku.java:30)
    at Sudoku.solve(Sudoku.java:38)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:67)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:38)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:38)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:38)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:67)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:67)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:38)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:38)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:71)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.solve(Sudoku.java:50)
    at Sudoku.main(Sudoku.java:9)

有什么想法可以停止循环并打印正确的字段吗? System.out.println(field) 而不是解决方法中的这个?

某处试图访问不存在的数组上层元素。尝试调试并找到。

可能 val 是要查找的关键。

我假设 for (int val = 1; val <=9; val++) 应该 for (int val = 1; val <9; val++)

永不终止的递归

(在这种情况下,它会在出错时终止)。

你的这部分代码需要实际做一些事情来表明它已经完成:

if ( j >= Field.SIZE) {

    //we are done

}

否则该循环只是继续进行,没有意识到它应该停止(缩短):

for (int val = 1; val <=9; val++) {

     ...
     
     solve (f, i+1, 0); // Maybe this call is 'done', but this loop will keep going

     ...
}

因此,基于 throws SolvedException 你的代码应该在那里抛出异常:

if ( j >= Field.SIZE) {

    //we are done
    throw new SolvedException();
    
}

但这是一个坏主意。例外情况是 not 用于控制代码流 - 它们用于 completely unexpected situations.

表示完成

相反,我们需要通过某种方式知道代码何时达到 'done' 条件。在经典递归中,这是通过 returning 某些东西来执行的。正如我们只想知道它是否完成,bool 处理得很好:

// Type changed to bool, removed throws:

public static bool solve(Field f, int i, int j) {

    System.out.print(f+"\n");

    if ( j >= Field.SIZE) {

        //we are done (return true now!)
        return true;

    } 

    if (f.isEmpty(i, j)) {
        
        for (int val = 1; val <=9; val++) {
            
            if (f.tryValue(val, i, j)){

                if (j>=Field.SIZE-1){
                    
                    if( solve (f, i+1, 0) ){
                        // This halts the loop here:
                        return true;
                    }

                    f.clear(i, j);
                    
                } else {
                    
                    if( solve(f,i,j+1) ){
                        // This halts the loop here:
                        return true;
                    }

                    f.clear(i, j);

                }

            }
        }
        
    } else if (j>=Field.SIZE-1) {
        
        // (Side note: This one is tail recursion)
        return solve(f,i+1,0);
        
    }
    
    // (Side note: This one is tail recursion)
    return solve(f,i,j+1);

}

在被调用者中,你还有这个:

try {
  solve(field, 0, 0);
} 
catch (SolvedException e) { }

你也会换成:

if( solve(field, 0, 0) ){

    // It was solved!
    
}

改为返回 void

您提到您仍然想要 return void。好的,所以,我们需要在其他地方跟踪 'done' 状态 - 例如在我们称之为 SudokuSolver:

的某个对象中
public class SudokuSolver{
    
    /* Set true when the solve is done */
    public bool done;
    
}

使用它使代码看起来更像这样:

// Type changed to void, added our solver arg:

public static void solve(Field f, int i, int j, SudokuSolver solver) {
    
    System.out.print(f+"\n");

    if ( j >= Field.SIZE) {

        //we are done (return true now!)
        solver.done=true;
        return;
        
    } 
    
    if (f.isEmpty(i, j)) {
        
        for (int val = 1; val <=9; val++) {
            
            if (f.tryValue(val, i, j)){

                if (j>=Field.SIZE-1){
                    
                    solve (f, i+1, 0, solver);
                    
                    if ( solver.done ) {
                        
                        // This halts the loop here:
                        return;
                    }

                    f.clear(i, j);
                    
                } else {
                    
                    solve(f,i,j+1, solver);
                    
                    if ( solver.done ) {
                        
                        // This halts the loop here:
                        return;
                    }

                    f.clear(i, j);

                }

            }
        }
        
    } else if (j>=Field.SIZE-1) {
        
        solve(f,i+1,0, solver);
        
    } else {
        
        solve(f,i,j+1, solver);
        
    }
    
}

呼叫站点现在看起来像这样:

SudokuSolver solver=new SudokuSolver();

solve(field, 0, 0, solver);

if ( solver.done ) {
    // It was solved!
}