一个数独谜题是最小的(也称为不可约)当且仅当它有唯一的解,但是移除任何数字都会产生一个有多个解的谜题。换句话说,每个数字都是必要的来确定解决方案。
我有一个生成最小数独的基本算法:
- 生成一个完成的谜题。 - 以随机顺序访问每个单元格。对于每个访问的单元格:
- 暂时删除其数字 - 使用递归回溯算法两次解决谜题。一个求解器按正向顺序尝试1-9的数字,另一个按相反的顺序。从某种意义上说,求解器正在遍历包含所有可能配置的搜索树,但从相反的方向。这意味着如果谜题有唯一解,则两个解决方案将匹配。 - 如果谜题有唯一的解,则永久删除数字;否则,将其放回。
这种方法保证产生最小的谜题,但速度很慢(在我的计算机上为100毫秒,在智能手机上为几秒钟)。我想减少解决数,但我能想到的所有明显的方法都是不正确的。例如:
- 添加数字而不是删除它们。这样做的好处是,由于最小谜题需要至少17个填充数字,因此前17个数字保证没有唯一的解决方案,从而减少了求解量。不幸的是,由于以随机顺序访问单元格,许多不必要的数字将在一个重要的数字“锁定”唯一解决方案之前被添加。例如,如果添加的前9个单元格都在同一列中,则存在大量冗余信息。 - 如果没有其他数字可以替换当前数字,请保留它并且不要解决谜题。因为检查放置是否合法比解决谜题快上千倍,这可能会节省大量时间。但是,只是因为现在没有其他合法数字并不意味着以后不会有,在我们删除其他数字后。 - 既然我们生成了原始解决方案,就只需要每个单元格解决一次,看看它是否与原始解决方案匹配。这种方法行不通,因为原始解决方案可能在所有可能解决方案的搜索树中的任何位置。例如,如果原始解决方案靠近树的“左侧”,并且我们从左侧开始搜索,我们将错过树的右侧的解决方案。
我也想优化解决算法本身。难点在于确定解决方案是否唯一。我可以进行微小的优化,例如为每个单元格创建合法放置的位掩码,如这篇绝妙的文章所述。但是,更高级的算法(如Dancing Links或模拟退火)不是设计用于确定唯一性,而只是找到任何解决方案。
我该如何优化我的最小数独生成器?
我有一个生成最小数独的基本算法:
- 生成一个完成的谜题。 - 以随机顺序访问每个单元格。对于每个访问的单元格:
- 暂时删除其数字 - 使用递归回溯算法两次解决谜题。一个求解器按正向顺序尝试1-9的数字,另一个按相反的顺序。从某种意义上说,求解器正在遍历包含所有可能配置的搜索树,但从相反的方向。这意味着如果谜题有唯一解,则两个解决方案将匹配。 - 如果谜题有唯一的解,则永久删除数字;否则,将其放回。
这种方法保证产生最小的谜题,但速度很慢(在我的计算机上为100毫秒,在智能手机上为几秒钟)。我想减少解决数,但我能想到的所有明显的方法都是不正确的。例如:
- 添加数字而不是删除它们。这样做的好处是,由于最小谜题需要至少17个填充数字,因此前17个数字保证没有唯一的解决方案,从而减少了求解量。不幸的是,由于以随机顺序访问单元格,许多不必要的数字将在一个重要的数字“锁定”唯一解决方案之前被添加。例如,如果添加的前9个单元格都在同一列中,则存在大量冗余信息。 - 如果没有其他数字可以替换当前数字,请保留它并且不要解决谜题。因为检查放置是否合法比解决谜题快上千倍,这可能会节省大量时间。但是,只是因为现在没有其他合法数字并不意味着以后不会有,在我们删除其他数字后。 - 既然我们生成了原始解决方案,就只需要每个单元格解决一次,看看它是否与原始解决方案匹配。这种方法行不通,因为原始解决方案可能在所有可能解决方案的搜索树中的任何位置。例如,如果原始解决方案靠近树的“左侧”,并且我们从左侧开始搜索,我们将错过树的右侧的解决方案。
我也想优化解决算法本身。难点在于确定解决方案是否唯一。我可以进行微小的优化,例如为每个单元格创建合法放置的位掩码,如这篇绝妙的文章所述。但是,更高级的算法(如Dancing Links或模拟退火)不是设计用于确定唯一性,而只是找到任何解决方案。
我该如何优化我的最小数独生成器?