创建数独谜题时出现重复问题

时间:2009-10-15 08:23:12

标签: algorithm deadlock puzzle sudoku

我正在尝试创建自己的普通9x9 sudoku puzzle

我将问题分成两部分 -

  1. 创建一个完全填充的数独和
  2. 删除不必要的号码 网格
  3. 现在,我被困在第一部分。


    这是我简要使用的算法:

    a)首先我选择一个数字(比如1),生成一个随机的单元格位置,然后将其放在那里

    • 该单元格尚未被占用,
    • 如果该行还没有该号码,
    • 如果列中还没有数字,
    • 如果3x3框还没有数字

    b)现在我检查一个连续,一个列或一个盒子中只有一个地方是空的情况,我填写了

    c)我检查如果有一个数字不在一个方框中但存在于同一行和同一列的方框中(我在这里谈论的是3x3方框),这个数字的位置是固定的,我填写它。

    d)我重复上述步骤,直到每个数字出现在网格上九次。


    我面临的问题是,我经常会遇到这样的中间情况:

     0  1  0 | 0  0  3 | 0[4/2]0 
     0 [2] 0 | 0 [4] 1 | 3  0  0 
     3  0 [4]|[2] 0  0 | 0  0  1 
    ---------+---------+---------
     2  0  3 | 0  5  4 | 0  1  0 
     0  0  1 | 3  0  2 |[4] 0  0 
     0  4  0 | 0  1  0 |[2] 3  0 
    ---------+---------+---------
     1  0  2 | 0  3  0 | 0  0 [4] 
     4  3  0 | 1  0  0 | 0  0 [2] 
     5  0  0 | 4  2  0 | 1  0  3
    

    查看[4/2]写的地方?因为标有[]的方框,这是2和4的位置。

    我可以做些什么来避免陷入这种情况(因为这种情况是僵局 - 我无法继续前进)

3 个答案:

答案 0 :(得分:4)

还有另一种方法来生成数独谜题:从一个已知的良好网格开始 - 任何人都会做 - 然后通过应用不破坏不变量的操作随机“洗牌”它。有效的操作包括:

  • 交换块内的行
  • 交换块内的列
  • 交换整行的块(例如,前3,中3,后3行)
  • 交换整列的块
  • 将一个号码的所有实例与另一个号码交换
  • 反映董事会
  • 旋转电路板

通过这些操作,您可以生成大量可能的电路板。然而,你需要注意如何应用这些操作 - 就像天真的洗牌一样,很容易编写一种算法,使某些板比其他板更有可能。类似于Knuth shuffle的技术可能对此有所帮助。

编辑:评论中指出,单靠这些操作不足以创建每个可能的网格。

答案 1 :(得分:1)

你将永远得到这种情况。您需要递归回溯搜索来解决它。

基本上,确定特定数字对单元格是否真正有效的唯一方法是继续搜索并查看会发生什么。

回溯搜索通常使用递归调用完成。每个调用将遍历一个单元格的(可能)仍然有效的选项,递归以评估下一个单元格的所有选项。当你无法继续时,回溯意味着从当前通话中返回 - 当然,首先删除你为该单元测试的任何数字。

当您找到有效的解决方案时,请保存并回溯以继续(即查找替代方案),或者中断所有递归调用以完成。成功进行递归回溯搜索是一种特殊情况,其中抛出成功的异常是IMO的一个好主意 - 特殊呼叫成功,并且代码将更清晰。

如果生成随机板,则以随机顺序迭代特定递归调用中的选项(对于特定单元格)。

同样的基本算法也适用于部分完成的板(即解决现有的sodoku) - 当评估已经有数字的单元时,那么,这是该单元的唯一选择,因此递归到下一个单元。

这是我曾经写过的一个求解器的回溯搜索 - 很多都是抽象出来的,但希望这只会使原理更清晰......

size_t Board::Rec_Search (size_t p_Pos)
{
  size_t l_Count = 0;

  if (p_Pos == 81)  //  Found a solution
  {
    l_Count++;

    std::cout << "------------------------" << std::endl;
    Draw ();
    std::cout << "------------------------" << std::endl;
  }
  else
  {
    if (m_Board [p_Pos] == 0)  //  Need to search here
    {
      size_t l_Valid_Set = Valid_Set (p_Pos);

      if (l_Valid_Set != 0)  //  Can only continue if there are possible digits
      {
        size_t  l_Bit = 1;  //  Scan position for valid set

        for (size_t i = 1; i <= 9; i++)
        {
          if (l_Valid_Set & l_Bit)
          {
            Set_Digit  (p_Pos, i);
            l_Count += Rec_Search (p_Pos + 1);
          }

          l_Bit <<= 1;
        }

        Clr_Digit (p_Pos);  //  Ensure cleared properly for backtracking
      }
    }
    else  //  Already filled in - skip
    {
      l_Count += Rec_Search (p_Pos + 1);
    }
  }

  return l_Count;
}

答案 2 :(得分:0)

如果你达到了一个矛盾的状态,如果一个单元格,如果你的2和4中的一些你的其他2和4都必须被错误地放置。您需要回滚并尝试一些不同的解决方案。

听起来你可能有算法问题?一些好东西here