【问题标题】:A better/good way to generate 4x4x4 Sudoku board? [closed]生成 4x4x4 数独板的更好/好方法? [关闭]
【发布时间】:2012-05-04 11:54:08
【问题描述】:

当我有时间的时候,为了好玩,我喜欢编写游戏代码来看看它有多难,或者只是因为我想从内部了解它是如何工作的,或者只是为了个人挑战。我只是喜欢编码。目前,我制作了these ones

所以我做了一个数独板。首先它是普通的 3x3x3 板,但后来有人让我做一个 4x4x4 板。我成功了,但我的算法很慢。

问题是,如何为 4x4x4(或更多)数独板制定快速算法

我当前的算法是这样工作的:我将用一个随机数填充网格 1,确保我不会放回相同的数字,然后切换到网格 2。当我处于 0.0 或任何其他位置时在网格中,我从网格 1 中删除任何可能的数字。对于网格中的每一行/列,我都会这样做。 (现在你可以看到英语不是我的第一语言,所以如果很难理解,我很抱歉。)


更新:
几个月后的一些跟进。

我目前的解决方案可以在here找到

所以当我开始这个问题时,它每 30-50 秒做 1 个网格,现在每秒超过 300 个

【问题讨论】:

  • 问题到底是什么?或者这只是一篇博客文章?如果只是一篇博文,请在 blogspot.com 上建立自己的博客
  • 现在 joel.neely 的方法似乎比我的方法更好,还有其他方法吗?如果不是,我会在本周晚些时候尝试他的建议时标记他的答案。
  • @S.Lott,问题是如何快速生成 4x4x4 或更大的数独网格
  • 从帖子中删除了 bloggish-rambling 以尝试使其成为一个更简洁的问题。
  • 我修复了我的代码,现在它比以前快得多,超过 80 格/秒。在每分钟 1 次之前

标签: .net algorithm optimization


【解决方案1】:

考虑以下网格:

 1  2  3  4 |  5  6  7  8 |  9 10 11 12 | 13 14 15 16
 5  6  7  8 |  9 10 11 12 | 13 14 15 16 |  1  2  3  4
 9 10 11 12 | 13 14 15 16 |  1  2  3  4 |  5  6  7  8
13 14 15 16 |  1  2  3  4 |  5  6  7  8 |  9 10 11 12
------------+-------------+-------------+------------
 2  3  4  1 |  6  7  8  5 | 10 11 12  9 | 14 15 16 13
 6  7  8  5 | 10 11 12  9 | 14 15 16 13 |  2  3  4  1
10 11 12  9 | 14 15 16 13 |  2  3  4  1 |  6  7  8  5
14 15 16 13 |  2  3  4  1 |  6  7  8  5 | 10 11 12  9
------------+-------------+-------------+------------
 3  4  1  2 |  7  8  5  6 | 11 12  9 10 | 15 16 13 14
 7  8  5  6 | 11 12  9 10 | 15 16 13 14 |  3  4  1  2
11 12  9 10 | 15 16 13 14 |  3  4  1  2 |  7  8  5  6
15 16 13 14 |  3  4  1  2 |  7  8  5  6 | 11 12  9 10
------------+-------------+-------------+------------
 4  1  2  3 |  8  5  6  7 | 12  9 10 11 | 16 13 14 15
 8  5  6  7 | 12  9 10 11 | 16 13 14 15 |  4  1  2  3
12  9 10 11 | 16 13 14 15 |  4  1  2  3 |  8  5  6  7
16 13 14 15 |  4  1  2  3 |  8  5  6  7 | 12  9 10 11

假设我没有打错字,很明显(从其构造模式来看)这符合数独布局的要求(每个值 1..16 在每一行、每一列、和 4x4 子网格)。

此外,很明显,以下每个更改都会满足要求(假设 1-origin 索引):

  1. 列交换:交换位于同一子网格内的任意两列的全部内容(例如交换列 1 和 3,交换列 10 和 11,但 不 交换第 6 列和第 13 列)。

  2. 行交换:交换位于同一子网格中的任意两列的全部内容(类似于 #1 的索引)。

  3. 子网格列交换:交换两列子网格的对应列(例如交换子网格列2和4,即交换所有列5和13,列6和14,列7 和 15,以及第 8 和 16 栏)。

  4. 子网格行交换:交换两行子网格的对应行(类似于#3的索引)。

因此,基于以上事实,策略是从上面显示的网格开始,然后进行一些合适的迭代次数(您可以通过实验确定),随机选择四种变换中的一种,并将其应用于满足转换要求的随机选择的索引。

例如,要应用变换#1,在范围 (1..4) 中随机选择子网格列号 sgcn,然后在范围 (1) 中随机选择两个不同的列号 cn1cn2 ..4)。交换 (sgcn - 1) * 4 + cn1(sgcn - 1) * 4 + cn2 列中的所有值。

通过从(任何)合法网格开始,并执行保持合法性的转换,可以保证结果是合法的。然而,随着应用的变换数量增加,人类观察者越来越难以区分模式和随机性。

用空白替换“打乱”网格中的值以获得所需的难度。

【讨论】:

  • 生成随机网格的有趣方式,我会在这周找到空闲时间时尝试一下,因为新年前夜应该很快!
  • 确实很有趣。我想知道:这个算法类似于数组的 Naive Shuffle,这往往表明像 naive shuffle 一样,它可能会生成一些比其他的频率更高的板。我想知道,数独是否有相当于 Knuth shuffle 的方法?
  • 很遗憾,它没有生成真正的随机网格
  • "用空白替换乱序网格中的值以获得所需的难度。"这比听起来更难,假设您希望得到的拼图只有 1 个解决方案。每次你做一个正方形的空白,你需要验证这个谜题仍然只有一个解决方案。
【解决方案2】:

你目前的算法是什么?

我想维基百科的Algorithmics of sudoku 页面上讨论的算法可以扩展到 4x4x4。

【讨论】:

  • 当我创建代码时,我没有看其他算法,因为我想自己做所有事情,但我确信没有很多方法可以做到。我会尽快更新我原来的帖子。
  • @Fredou,你想自己做这一切,但你问我们?那没有意义。如果您不再想“自己”做,那么请尝试实现维基百科页面中的一种已知算法。
  • 我会说我的算法看起来像上面 url 的“随机搜索/优化方法”,对于 9x9,它需要不到一秒钟,但问题是当我尝试做 16x16 (4x4x4)可能需要几分钟
  • 我的问题的结果可以在这里找到codeproject.com/KB/vb/CodeBehindSudoku.aspx,谢谢!
【解决方案3】:

可以使用用 Python 编写的 Sudoku Generator。关于作者如何生成数独板的解释(算法)在该页面上,并提供了源代码。

【讨论】:

  • 快速查看代码显示硬编码值为 3x3x3 网格
  • @Fredou,只需调整值。 =]
  • @strager,很遗憾我不是 python 程序员,看来这不仅仅是将 81 更改为 256 的问题......
【解决方案4】:

这是 joel.neely 建议的代码

3x3x3 传 3,4x4x4 传 4,等等

Public Class Class1

Private aSquare(,,) As Integer
Private iSquare As Integer

Sub New(ByVal squareSize As Integer)

    iSquare = squareSize
    ReDim aSquare(iSquare - 1, iSquare - 1, CInt(iSquare ^ 2 - 1))

    createSquare()
    rndSquare()

End Sub



Private Sub createSquare()
    Dim i As Integer, j As Integer, k As Integer

    For i = 0 To aSquare.GetUpperBound(0)
        For j = 0 To aSquare.GetUpperBound(1)
            For k = 0 To aSquare.GetUpperBound(2)
                If i = 0 And j = 0 Then
                    aSquare(i, j, k) = k + 1
                ElseIf j = 0 And i > 0 Then
                    If (k + i) Mod (iSquare) = 0 Then
                        aSquare(i, j, k) = aSquare(i - 1, j, k) - (iSquare - 1)

                    Else
                        aSquare(i, j, k) = aSquare(i - 1, j, k) + 1
                    End If
                Else
                    aSquare(i, j, k) = aSquare(i, j - 1, k) + iSquare
                End If

                If aSquare(i, j, k) > iSquare ^ 2 Then
                    aSquare(i, j, k) = aSquare(i, j, k) - CInt(iSquare ^ 2)
                End If
            Next
        Next
    Next
End Sub

Private Sub rndSquare()
    Dim i As Integer

    Randomize()

    For i = 0 To CInt(iSquare ^ 2)

        Select Case CInt(Rnd() * 3)
            Case 0
                rndBigCol()
            Case 1
                rndBigRow()
            Case 2
                rndLittleCol()
            Case 3
                rndlittleRow()
        End Select

    Next



End Sub

Private Sub rndBigCol()
    Dim square As Integer
    Dim rnd1, rnd2 As Integer
    Dim i As Integer, j As Integer, k As Integer

    Randomize()

    For k = 0 To iSquare
        Do
            rnd1 = CInt(Rnd() * aSquare.GetUpperBound(1))
            rnd2 = CInt(Rnd() * aSquare.GetUpperBound(1))
        Loop Until rnd1 <> rnd2

        For i = 0 To aSquare.GetUpperBound(0)
            For j = 0 To aSquare.GetUpperBound(2)
                square = aSquare(i, rnd1, j)
                aSquare(i, rnd1, j) = aSquare(i, rnd2, j)
                aSquare(i, rnd2, j) = square
            Next
        Next
    Next
End Sub

Private Sub rndBigRow()
    Dim square As Integer
    Dim rnd1, rnd2 As Integer
    Dim i As Integer, j As Integer, k As Integer

    Randomize()

    For k = 0 To iSquare
        Do
            rnd1 = CInt(Rnd() * aSquare.GetUpperBound(0))
            rnd2 = CInt(Rnd() * aSquare.GetUpperBound(0))
        Loop Until rnd1 <> rnd2

        For i = 0 To aSquare.GetUpperBound(1)
            For j = 0 To aSquare.GetUpperBound(2)
                square = aSquare(rnd1, i, j)
                aSquare(rnd1, i, j) = aSquare(rnd2, i, j)
                aSquare(rnd2, i, j) = square
            Next
        Next
    Next
End Sub

Private Sub rndLittleCol()
    Dim square As Integer
    Dim rnd1, rnd2, rnd3 As Integer
    Dim i As Integer, k As Integer, l As Integer

    Randomize()

    For k = 0 To iSquare * 2
        Do
            rnd1 = CInt(Rnd() * aSquare.GetUpperBound(1))
            rnd2 = CInt(Rnd() * (iSquare - 1))
            rnd3 = CInt(Rnd() * (iSquare - 1))
        Loop Until rnd2 <> rnd3

        For i = 0 To aSquare.GetUpperBound(0)
            For l = 0 To (iSquare - 1)
                square = aSquare(i, rnd1, rnd2 + (l * iSquare))
                aSquare(i, rnd1, rnd2 + (l * iSquare)) = aSquare(i, rnd1, rnd3 + (l * iSquare))
                aSquare(i, rnd1, rnd3 + (l * iSquare)) = square
            Next
        Next
    Next
End Sub

Private Sub rndlittleRow()
    Dim square As Integer
    Dim rnd1, rnd2, rnd3 As Integer
    Dim j As Integer, k As Integer, l As Integer

    Randomize()

    For k = 0 To iSquare * 2
        Do
            rnd1 = CInt(Rnd() * aSquare.GetUpperBound(0))
            rnd2 = CInt(Rnd() * (iSquare - 1))
            rnd3 = CInt(Rnd() * (iSquare - 1))
        Loop Until rnd2 <> rnd3

        rnd2 *= iSquare
        rnd3 *= iSquare

        For j = 0 To aSquare.GetUpperBound(1)
            For l = 0 To (iSquare - 1)
                square = aSquare(rnd1, j, rnd2 + l)
                aSquare(rnd1, j, rnd2 + l) = aSquare(rnd1, j, rnd3 + l)
                aSquare(rnd1, j, rnd3 + l) = square
            Next
        Next
    Next
End Sub
End Class

【讨论】:

    【解决方案5】:

    好的,我知道为什么 3x3x3 的速度如此之快,但 4x4x4 的速度却很慢

    这是我的回溯逻辑,非常糟糕,现在最多需要 20 秒(至少在我的电脑上),其中大部分时间不到 5 秒

    【讨论】:

      猜你喜欢
      • 2013-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-24
      • 1970-01-01
      • 2010-11-01
      相关资源
      最近更新 更多