【问题标题】:Fork/Join Sudoku Solver分叉/加入数独求解器
【发布时间】:2016-12-29 17:35:56
【问题描述】:

我的练习有问题。我需要使用 fork/join 并行性找到给定数独的所有解决方案。我做了一个算法,但它似乎不起作用。它在某个时候停止,我不知道为什么。

代码如下:

private static int counter;
private Cella[][] sudoku;
private int i;
private int j;
private int theCounter = 0;

public SudokuMulti(Cella[][] sudoku) {
    this.sudoku = sudoku;
}

public SudokuMulti(Cella[][] sudoku, int i, int j) {
    this.sudoku = sudoku;
    this.i = i;
    this.j = j;
}

//DELETED

// Copy the sudoku matrix
private Cella[][] createCopy() {
    Cella[][] toReturn = new Cella[9][9];
    for (int i = 0; i < 9; i++) {
        System.arraycopy(sudoku[i], 0, toReturn[i], 0, 9);
    }
    return toReturn;
}

以及Cella对象的代码:

public class Cella {

private int current;

public Cella() {
    current = 0;
}

public Cella(int current) {
    this.current = current;
}

//Getter and Setter

鉴于候选单元格的“法律价值”,我的想法是赋予每个线程解决其自身数独问题的能力。然后我收集一个 ArrayList 中的所有线程,并要求它们与最后一个 fork 分叉。每个线程都应该返回一个整数(0 表示不成功,1 表示成功),以便计算可以解决多少个可能的数独。

但是,该算法只覆盖了 1/3 的数独:在某个点之后,它停止填充单元格,它只是返回而不完成它。

有人可以建议我在哪里做错了吗?

【问题讨论】:

  • 该方法中使用的方法和字段在示例中没有提供。这使得这些其他方法看起来很可能修改这些字段或调用其他方法,从而使解决您的问题更加困难。你能提供完整的课程吗?
  • 我不知道这是否能解决你的失败问题,但你产生多个求解器线程的循环似乎犯了一个可怕的错误。您创建当前状态的副本,然后将内部状态传递给SudokuMulti 而不是副本!这意味着子进程正在修改调用者的状态。我相当肯定你需要那个块像Cella[][] copy = createCopy(); copy[I][j].setCurrent(v); forkedThreads.add(new SudokuMulti(copy, i + 1, j).fork());
  • @sadakatsu 完成,谢谢
  • @sadakatsu 我尝试了你的建议,但它不起作用。
  • 1) 您提供的代码仍然不完整。例如,SudokuMulti 似乎实际上并没有扩展ForthJoinTask,因为compute() 不是该类的方法。最小可验证示例 (MVE) 会有所帮助。 2)也许您的应用程序失败了,因为您正在排队 81^9 个任务。 compute() 是否抛出了任何 RuntimeException 子类? ForkJoinPool 的文档说:“只有在池关闭或内部资源耗尽时,此实现才会拒绝提交的任务(即通过抛出 RejectedExecutionException)。”

标签: java parallel-processing sudoku fork-join forkjoinpool


【解决方案1】:

根据您发布的代码,我看不到任何可以解释您的问题的问题。但是,您还没有发布我可以自己编译和执行的代码(称为最小工作或可验证示例,请参阅WikipediaStackOverflow's guide on creating one),也没有发布您的应用程序的堆栈跟踪或输出。这使得帮助您解决问题变得困难。如果您能提供更多,我愿意继续帮助您解决问题。

与此同时,我尝试按照您的方法编写一个解决相同问题的程序。它似乎有效,尽管我没有对它进行彻底的单元测试。也许您可以将其与您编写的内容进行比较,并使用差异来发现问题。您至少需要 Java 7 才能编译和运行此代码。

如果这是家庭作业,我建议在查看此列表之前咨询您的教授或助教。

public class Main {
    public static void main( String[] args ) {
        Sudoku puzzle = new Sudoku();
          // Uncomment these lines to have a uniquely solvable Sudoku puzzle.  They are commented out to prove that this code can count multiple solutions.
//        puzzle.set(1, 0, 2);
//        puzzle.set(2, 0, 9);
//        puzzle.set(4, 0, 5);
//        puzzle.set(7, 0, 4);
//        puzzle.set(8, 0, 1);
//        puzzle.set(3, 1, 8);
//        puzzle.set(6, 1, 3);
//        puzzle.set(2, 2, 3);
        puzzle.set(3, 2, 7);
        puzzle.set(4, 2, 4);
        puzzle.set(5, 2, 9);
        puzzle.set(6, 2, 6);
        puzzle.set(3, 3, 4);
        puzzle.set(6, 3, 2);
        puzzle.set(7, 3, 1);
        puzzle.set(1, 4, 6);
        puzzle.set(3, 4, 3);
        puzzle.set(4, 4, 7);
        puzzle.set(5, 4, 1);
        puzzle.set(7, 4, 8);
        puzzle.set(1, 5, 4);
        puzzle.set(2, 5, 1);
        puzzle.set(5, 5, 6);
        puzzle.set(2, 6, 5);
        puzzle.set(3, 6, 9);
        puzzle.set(4, 6, 2);
        puzzle.set(5, 6, 8);
        puzzle.set(6, 6, 7);
        puzzle.set(2, 7, 4);
        puzzle.set(5, 7, 7);
        puzzle.set(0, 8, 3);
        puzzle.set(1, 8, 7);
        puzzle.set(4, 8, 6);
        puzzle.set(6, 8, 5);
        puzzle.set(7, 8, 2);

        SudokuSolver solver = new SudokuSolver(puzzle);
        long start = System.nanoTime();
        int totalSolutions = solver.compute();
        long end = System.nanoTime();
        System.out.println(totalSolutions);
        System.out.format("%f ms", (end - start) / 1e6);
    }

    private static class Sudoku {
        private final int[][] cells;

        Sudoku() {
            cells = new int[9][9];
        }

        Sudoku( Sudoku original ) {
            cells = new int[9][9];
            for (int column = 0; column < 9; ++column) {
                for (int row = 0; row < 9; ++row) {
                    set(column, row, original.get(column, row));
                }
            }
        }

        int get( int column, int row) {
            return cells[column][row];
        }

        void set( int column, int row, int value ) {
            cells[column][row] = value;
        }

        boolean isPlausible() {
            return columnsArePlausible() && rowsArePlausible() && blocksArePlausible();
        }

        private boolean columnsArePlausible() {
            boolean result = true;

            for (int column = 0; result && column < 9; ++column) {
                result = isColumnPlausible(column);
            }

            return result;
        }

        private boolean isColumnPlausible( int column ) {
            boolean result = true;

            boolean[] seen = new boolean[10];
            for (int row = 0; result && row < 9; ++row) {
                int value = get(column, row);
                if (value > 0 && seen[value]) {
                    result = false;
                } else {
                    seen[value] = true;
                }
            }

            return result;
        }

        private boolean rowsArePlausible() {
            boolean result = true;

            for (int row = 0; result && row < 9; ++row) {
                result = isRowPlausible(row);
            }

            return result;
        }

        private boolean isRowPlausible( int row ) {
            boolean result = true;

            boolean[] seen = new boolean[10];
            for (int column = 0; result && column < 9; ++column) {
                int value = get(column, row);
                if (value > 0 && seen[value]) {
                    result = false;
                } else {
                    seen[value] = true;
                }
            }

            return result;
        }

        private boolean blocksArePlausible() {
            boolean result = true;

            for (int column = 0; result && column < 9; column += 3) {
                for (int row = 0; result && row < 9; row += 3) {
                    result = isBlockPlausible(column, row);
                }
            }

            return result;
        }

        private boolean isBlockPlausible( int column, int row ) {
            boolean result = true;

            boolean[] seen = new boolean[10];
            for (int x = 0; result && x < 3; ++x) {
                for (int y = 0; result && y < 3; ++y) {
                    int value = get(column + x, row + y);
                    if (value > 0 && seen[value]) {
                        result = false;
                    } else {
                        seen[value] = true;
                    }
                }
            }

            return result;
        }
    }

    private static class SudokuSolver extends RecursiveTask<Integer> {
        private static final long serialVersionUID = 8759452522630056046L;

        private Sudoku state;
        private int column;
        private int row;

        SudokuSolver( Sudoku state ) {
            this.state = state;
            // These settings allow the search loop in compute() to increment first without asking questions about
            // whether this cell has been checked yet.
            column = -1;
            row = 8;
        }

        SudokuSolver( Sudoku state, int column, int row ) {
            this.column = column;
            this.row = row;
            this.state = state;
        }

        @Override
        protected Integer compute() {
            int viableSolutions = 0;

            if (state.isPlausible()) {
                int originalColumn = column;
                int originalRow = row;

                do {
                    if (row + 1 >= 9) {
                        ++column;
                        row = 0;
                    } else {
                        ++row;
                    }
                } while (column < 9 && state.get(column, row) != 0);

                if (column >= 9) {
                    viableSolutions = 1;
                } else {
                    List<SudokuSolver> solvers = new ArrayList<>();
                    for (int value = 1; value <= 9; ++value) {
                        Sudoku copy = new Sudoku(state);
                        copy.set(column, row, value);
                        solvers.add(new SudokuSolver(copy, column, row));
                    }
                    invokeAll(solvers);
                    for (SudokuSolver solver : solvers) {
                        viableSolutions += solver.join();
                    }
                }
            }

            return viableSolutions;
        }
    }
}

由于此代码乘以计算解决方案所需的时间,因此输出可能会有所不同,但我得到了

354
709.848410 ms

【讨论】:

  • 感谢您的宝贵时间。我弄清楚是什么问题,这很尴尬。由于矩阵复制过程中的错误,它不起作用。当我复制数组时,我用Cella 对象引用而不是新的对象引用来填充它,所以它会导致数据竞争。
【解决方案2】:

我找到了解决方案。这是错误:

// Copy the sudoku matrix
private Cella[][] createCopy() {
    Cella[][] toReturn = new Cella[9][9];
    for (int i = 0; i < 9; i++) {
        // !!ERROR!!
        System.arraycopy(sudoku[i], 0, toReturn[i], 0, 9);
    }
    return toReturn;
}

当我复制数组时,我用 Cella 对象引用而不是新的引用来填充它,所以它会导致数据竞争。

复制矩阵的正确方法是:

private Cella[][] createCopy() {
    Cella[][] toReturn = new Cella[9][9];
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            toReturn[i][j] = new Cella(sudoku[i][j].getCurrent());
        }
    }
    return toReturn;
}

【讨论】:

    猜你喜欢
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2013-03-08
    相关资源
    最近更新 更多