【问题标题】:Sudoku generation speed数独生成速度
【发布时间】:2015-11-05 18:43:55
【问题描述】:

我编写了一个回溯算法来生成任意大小的数独。我的问题是这个生成算法的速度变化。

我添加了一些控制台输出来查看结果。我生成了大约 20 个数独,大小为 16x16,每个数独的生成时间为 1 毫秒到 150 毫秒。下一个花了10多分钟,甚至没有完成。 (我停止了程序)。考虑到这一点,我认为我在回溯实现中犯了一个错误,导致算法无法前进。

代码如下:

public class Generator {
    public static NormalSudoku generateNormal(int size) {
    NormalSudoku ns = new NormalSudoku(size);
    boolean[][][] track = new boolean[size][size][size];
    ArrayList<Integer> vals = new ArrayList<Integer>();
    fill(vals, size);

    int row = 0;
    int col = 0;
    int val = 0;

    int count = 0;
    while (row < size) {
        boolean found = false;
        while (!vals.isEmpty() && !found) {
            val = vals.get((int) (Math.random() * vals.size()));
            track[row][col][val - 1] = true;
            if (ns.checkValue(row, col, val)) {
                 ns.setValue(row, col, val);
                 fill(vals, size);
                 col++;
                found = true;
            } else {
                 vals.remove(Integer.valueOf(val));
            }

            if (col >= size) {
                 row++;
                 col = 0;
            }
        }
        if (vals.isEmpty()) {
        ns.setValue(row, col, 0);
        for (int i = 0; i < size; i++) {
            track[row][col][i] = false;
        }
        col--;
        if (col < 0) {
            row--;
            col = size - 1;
        }
        ns.setValue(row, col, 0);
        fill(vals, track, row, col);
        }
    }
    return ns;
    }

    private static void fill(ArrayList<Integer> vals, int size) {
    vals.clear();
    for (int i = 1; i <= size; i++) {
        vals.add(i);
    }
    }

    private static void fill(ArrayList<Integer> vals, boolean[][][] track,
        int row, int col) {
    vals.clear();
    for (int i = 0; i < track[row][col].length; i++) {
        if (!track[row][col][i]) {
        vals.add(Integer.valueOf(i + 1));
        }
    }
    }
}

代码说明:

boolean[][][] 轨道:

已经尝试过的值的某种清单(以减少多次尝试相同随机值的开销)。如果我已经尝试将 val 放在该字段上,我将 track[row][col][val] 放在 true 上。如果我回去,我必须清除该单元格的标记,以使值再次成为可能。

ns.checkValue:

在单元格上设置值,检查数独是否仍然正确,再次设置旧值并返回真/假。

ns.setValue:

只需在单元格上设置值。

值:

包含尚未对该单元格尝试的值的数组列表。如果我继续前进,列表显然必须包含所有可能的数字。 如果我回溯,列表只需要包含尚未尝试的数字。 (我使用轨道数组来实现这一点)。

我做错了什么?

问候

【问题讨论】:

  • 你可能想把它发到codereview
  • 在我早期的编程生涯中,我也尝试过制作一个预填充数独程序,首先你需要分析一个完全填充数独的所有可能规则,你会发现仅仅回溯是不够的,并且这就是为什么有时程序永远不会完成的原因,我也面临同样的情况......

标签: java algorithm performance sudoku


【解决方案1】:

好吧,由于某种原因,您首先从 vals 列表中选择了一个随机数,该随机数大部分时间都充满了 10 多个数字,因此您在那里浪费了很多时间。 我测试了你的代码,解决的持续时间真的很随机​​。 例如关于这个临时解决方案

[12, 9, 14, 7, 5, 13, 8, 2, 15, 3, 4, 6, 16, 1, 10, 11]
[1, 2, 13, 15, 12, 16, 14, 4, 11, 8, 10, 9, 5, 6, 0, 0]
....

填充单元格 (2,15) 和 (2,16) 需要花费太多时间,因为它会从列表 val 中随机选择 10 个值,但可能的值是数字 3 和 7。

在 vals 列表中,您应该排除当前行、当前列和区域中的数字。

但是这并没有对算法有太大的改进。

你的算法是:

1. For current cell try all values until you find one that fits.
2. If it fits track it and move on to next cell.
3. If no value fits take step back, try value on the previous cell that was not used yet.

由于随机性,如果某些值在某些最初的位置上不是很好,那么您在第 3 点上花费了太多时间。

【讨论】:

  • 好吧,既然我想生成(而不是解决)它们,我需要一些随机性,否则我总是会生成相同的。谢谢你的 vals 列表,我没看到。
  • 我的下一个建议是你在 vals.add(i);在填充方法中。此外,您正在使用 for 循环将整数添加到列表中,这有点慢,因为您可以将整数 [1,大小] 存储在列表变量 allValues 中,当您填充时执行 vals.clear() vals.addAll(allValues)。 addAll 比循环添加快得多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-15
  • 2010-12-30
  • 1970-01-01
  • 2015-12-23
相关资源
最近更新 更多