简单回溯暴力算法最坏情况下有效的数独谜题是什么?

2024-03-10

The "简单/幼稚的回溯暴力算法”、数独的“直接深度优先搜索”是众所周知并已实现的。

并且似乎不存在不同的实现。 (当我第一次写这个问题时..我想说我们可以完全标准化它,但措辞很糟糕..)

我认为这个人很好地描述了算法:https://stackoverflow.com/a/2075498/3547717 https://stackoverflow.com/a/2075498/3547717

编辑:所以让我用伪代码更详细地说明它......

var field[9][9]
set the givens in 'field'
if brute (first empty grid) = true then
    output solution
else
    output no solution
end if

function brute (cx, cy)
    for n = 1 to 9
        if (n doesn't present in row cy) and (n doesn't present in column cx) and (n doesn't present in block (cx div 3, cy div 3)) then
            let field[cx][cy] = n
            if (cx, cy) this is the last empty grid then
                return true
            elseif brute (next empty grid) = true then
                return true
            end if
            let field[cx][cy] = empty
        end if
    next n
end function

我想找到最需要时间的谜题。对于这种特定的“标准化”算法,我们可以称其为“最难”,但这并不像那些要求“最难的数独”的问题。

事实上,只要简单地旋转或翻转,这个定义下的“困难”谜题就可能变得超级简单。

根据“对于每个网格尝试数字1到9”的规则,它从1开始尝试,所以我们可以以某种方式让它通过使用适当的数字尝试更多,这样就不会出现排列问题。

数独谜题必须是valid,即它应该有 1 个解。Some guy http://norvig.com/sudoku.html有一个谜题需要 1439 秒,但由于没有答案而无效。

我定义所需的时间(或者说时间复杂度)相当于输入递归函数的次数。 (在我的实现中,它与上面的伪代码略有不同,因为最后一个入口,并确保唯一的解决方案等)

有没有什么好的方法来构造它,或者我们必须使用启发式算法等近似方法来找到不精确的解决方案?


我已经使用朴素策略(我在上面称为“简单”,它是唯一的)和 Peter Norvig 的“最少候选者优先”策略(我的实现是确定性的,但不是唯一的。正如 Peter 也提到的那样)实现了回溯。 python dict 的顺序会极大地改变结果,以防候选人数量平局)。

https://github.com/farteryhr/labs/blob/master/sudoku.c https://github.com/farteryhr/labs/blob/master/sudoku.c

无解方案一:

.....5.8....6.1.43.........1.5.........1.6...3.......553..... 61.........4.........

在我的笔记本电脑上花了 60 秒才得出无解结论,输入递归函数 2549798781 次(后面称为“循环”)。通过我的 LCF 实现,78308087 个循环在 30 秒内结束。这是因为寻找候选数最少的网格需要更多的操作,LCF策略的单个周期使用大约16倍的时间。

最难列表中最上面的一个:

4.....8.5.3......7......2......6.....8.4......1...... ...6.3.7.5..2.....1.4......

耗时3.0s,在第9727397个周期找到解,第142738236个周期确保解唯一。 (我的 LCF:0.004 秒内为 981/7216)

“困难”列表中的许多仍然很容易天真,尽管其中很大一部分需要 10^7 到 10^9 周期。

On 维基百科:数独求解算法 https://en.wikipedia.org/wiki/Sudoku_solving_algorithms#Backtracking (Original https://www.flickr.com/photos/npcomplete/2361922699)据说可以通过在开始时制作尽可能多的空网格和顶行 987654321 的排列来构造此类针对回溯算法的谜题。

好在测试..

......3.85..1.2......5.7.....4...1...9.......5.. ....73..2.1........4...9

需要1.4s,69175317个周期寻找解决方案,69207227个周期确保唯一解决方案。不如Peter提供的难点,但是还好,差不多找到解决方案后,搜索就结束了。这可能就是第一行按字典顺序排列的大小的工作原理。 (我的 LCF:0.023 秒内为 29206/46160)

是的,这些是显而易见的,我只是要求更好的方法......


还有其他方法 https://www.nature.com/articles/srep00725衡量数独难度的方法(通过求解)


数独分析师 http://www.stolaf.edu/people/hansonr/sudoku/analyst.htm会陷入Peter给出的多解谜题(天真的419195/419256,LCF 2529478/2529482,是的,有一些谜题让LCF做得更糟):

.....6..59.....82....8....45.........3.........6..3.54.. .325..6.................

这对于朴素回溯 (10008/76703) 和 LCF 回溯 (313/1144) 来说都很容易,但也会让 Sudoku Analyst 陷入困境。

..53.....8......2..7..1.5..4....53...1..7...6..32...8.. 6.5....9..4..3......97..


另一个更新:

最困难的数独谜题可通过简单的深度优先搜索算法快速解决 https://www.dcc.fc.up.pt/%7Eacm/sudoku.pdf

哈,终于有人也在找了,而且还给了一个超难的!以下有效谜题:

9..8......5......2..1...3.1.....6..4...... 7.7.86..3.1..4..2..

本文将该算法命名为SDFS,Straightforward Depth-First Search。作者所说的周期数是1553023932/1884424814,而我的实现是1305263522/1584688020。是的,在弹出计数器的确切位置上会有一些差异,但基本行为是一致的。在 repl.it 的服务器上,找到答案花了 97 秒,完成搜索花了 119 秒。


您可以通过记录所花费的时间/次数轻松生成最坏的情况。您的代码为解决困难的数独难题而执行的操作。您可以使用随机生成器来生成有效的数独谜题(或者)您可以从互联网上获取困难的数独谜题并对其运行代码以测量操作的时间/数量。一旦针对 10000 个此类情况运行代码,最慢的 5 个(以及未解决的情况)将是您的解决方案的最差情况。

在farter的评论后编辑

我意识到你想提高算法的效率。考虑到 CPU 能力相当高,一个 su-do-ku 谜题需要 97 秒!您可以减少时间

  1. 不要使用回溯,仅填写您知道解决方案的方格。编写一个贪心算法,为您永远不会改变的方块找到答案。
  2. 并行化您的搜索(同时查找多个方块的解决方案)
  3. 你的搜索应该涉及多种贪婪的方法......对于。例如。选择显示最多数字的线条/3x3 正方形/3x9/9x3 矩形,您更有可能显示该区域中的数字,这将减少整个拼图的运行时间。还可以找到特定区域中每个数字的频率,这将有助于快速查找数字。例如,如果在 3x9 正方形中显示出两个 3,则您更有可能显示第三个 3,因为该区域中的第三个 3 应该位于 3 个单元格之一中。
  4. 对于困难的谜题,添加记忆......例如。在某些情况下,您知道某一对号码适合一对单元格。但你不知道哪个细胞进入哪个细胞。在这种情况下,您可以确定其他 7 个数字不会出现在这些单元格中。记住这个否定标准并利用它来最大限度地减少搜索时间。

让我知道这是否有帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

简单回溯暴力算法最坏情况下有效的数独谜题是什么? 的相关文章

  • 通往楼梯顶部的可能路径

    这是一个非常经典的问题 我听说谷歌在他们的面试中使用过这个问题 问题 制定一个递归方法 打印从楼梯底部到楼梯顶部的所有可能的独特路径 有 n 个楼梯 您一次只能走 1 步或 2 步 示例输出 如果它是一个有 3 级楼梯的楼梯 1 1 1 2
  • Florian 的 Grisu2 算法如何工作?

    我遇到了一个关于将 double 转换为 ascii 的问题 经过搜索 我得到了 Florian 的论文 使用整数快速准确地打印浮点数 http www cs tufts edu nr cs257 archive florian loits
  • 洪水填充优化:尝试使用队列

    我正在尝试创建一种填充方法 该方法采用用户指定的初始坐标 检查字符 然后根据需要更改它 这样做之后 它会检查相邻的方块并重复该过程 经过一番研究 我遇到了洪水填充算法并尝试了该算法 它可以工作 但无法满足我对 250 x 250 个字符的数
  • 为什么Racket中foldl的定义方式很奇怪?

    在 Haskell 中 与许多其他函数式语言一样 函数foldl被定义为 例如 foldl 0 1 2 3 4 10 这没关系 因为foldl 0 1 2 3 4 根据定义 0 1 2 3 4 但是 在 球拍 中 foldl 0 1 2 3
  • 举例解释bpe(字节对编码)?

    有人可以帮忙解释一下背后的基本概念吗BPE模型 除了这张纸 https arxiv org abs 1508 07909 目前还没有那么多解释 到目前为止我所知道的是 它通过将罕见和未知的单词编码为子词单元序列来实现开放词汇表上的 NMT
  • 线程“main”中的异常 java.lang.StackOverflowError

    我有一段代码 但我无法弄清楚为什么它在线程 main java lang StackOverflowError 中给出异常 这是问题 Given a positive integer n prints out the sum of the
  • 每个术语出现的次数

    我得到了一个数组a n 2 where n can be 10 5最大时有n个科目和n个学生 全部编号为 1 2 n a i 0 and a i 1 1 lt i lt n 表示在第 i 个科目中 所有来自a i 0 to a i 1 通过
  • 递归修剪对象中所有元素的更好方法?

    如果我有一个像这样的物体 const obj field subfield innerObj a asdasd asdas innerArr s ssad innerArrObj b adsad 我想出了这样的东西 const trimFi
  • O(1) 算法确定节点是否是多路树中另一个节点的后代?

    想象一下下面的树 A B C D E F 我正在寻找一种方法来查询 F 是否是 A 的后代 注意 F 不需要是directA 的后代 在这种特殊情况下这是正确的 只需要针对更大的潜在后代节点池测试有限数量的潜在父节点 当测试一个节点是否是潜
  • 归并排序中的递归:两次递归调用

    private void mergesort int low int high line 1 if low lt high line 2 int middle low high 2 line 3 mergesort low middle l
  • 选择一组数字以达到最小总数的算法

    给定 一组数字n 1 n 2 n 3 n x 还有一个数字M 我想找到最好的组合 n a n b n c n gt M 该组合应达到达到或超过 M 所需的最小值 没有其他组合可以提供更好的结果 将在 PHP 中执行此操作 因此可以使用 PH
  • 列出所有 k 元组,其条目总和为 n,忽略旋转

    有没有一种有效的算法来查找所有序列k总和为的非负整数n 同时避免旋转 如果可能的话 完全避免 顺序很重要 但对于我正在解决的问题来说 轮换是多余的 例如 与k 3 和n 3 我想要得到一个如下所示的列表 3 0 0 2 1 0 2 0 1
  • 如何在 javascript 正则表达式中匹配平衡分隔符?

    我原以为这个问题是不可能的 据我所知 Javascript 的正则表达式既没有递归插值 也没有漂亮的 NET 平衡组功能 但问题就在那里 如问题 12 所示正则表达式 alf nu http regex alf nu 匹配平衡对 lt an
  • AStar-名称解释

    我正在寻找 AStar A 算法为何被称为 AStar 的解释 所有类似的 最短路径问题 算法通常都以其开发者的名字命名 那么 AStar 代表什么 有称为 A1 和 A2 的算法 后来证明A2是最优的 实际上也是可能的最好算法 所以他给它
  • 卷积函数可以写成尾递归形式吗?

    我有一个函数 我想以尾递归形式编写 该函数计算求和的方法数k通过滚动s双面模具n次 我已经在上面看到了这个函数的数学解这个答案 https math stackexchange com questions 397689 why convol
  • 找到一个数是素数,为什么检查到n/2更好。避免n后半部分的数字的原因是什么

    要检查一个数是否是素数 最简单的方法是尝试将这个数除以 2 到 n 如果任何操作得到余数为 0 那么我们就说给定的数不是素数 但最好只进行划分和检查直到 n 2 我知道更好的方法是直到 sqrt n 我想知道跳过后半部分的原因 假设我们是否
  • 欧拉项目 45

    我还不是一名熟练的程序员 但我认为这是一个有趣的问题 我想我应该尝试一下 三角形 五边形 六边形 数字由以下生成 公式 三角形 T n n n 1 2 1 3 6 10 15 五边形 P n n 3n 1 2 1 5 12 22 35 六角
  • 如何使用递归查找数字中的最小元素 [C]

    好的 所以我正在准备我的 C 考试 当谈到递归时我有点卡住了我是大学一年级的学生 这对我来说似乎有点困难 练习要求在给定的数字中使用递归函数我需要找到最小的元素 例如 52873 是 2 程序需要打印 2 include
  • 如何在 Unity 中对齐“轨道”或模块化对象?

    我正在开发一个简单的游戏 用户可以在其中放置不同但模块化的对象 例如 轨道 道路等 我的问题是 当将一个物体靠近另一个物体时 如何匹配和放置不同的物体 我的第一种方法是为每个模块对象创建一个隐藏的子对象 一个盒子 并将其放在可以放置其他对象
  • 滚动或滑动窗口迭代器?

    我需要一个可在序列 迭代器 生成器上迭代的滚动窗口 又名滑动窗口 默认的 Python 迭代可以被视为一种特殊情况 其中窗口长度为 1 我当前正在使用以下代码 我怎样才能更优雅和 或更有效地做到这一点 def rolling window

随机推荐