理解这个C函数

时间:2010-11-28 16:33:17

标签: c algorithm function latin-square

我试图了解这个功能是如何工作的,我已经研究了几种算法来生成数独游戏,并找到了这个。

测试了该函数,它确实生成了一个有效的9x9拉丁方(Sudoku)网格。 我的问题是我无法理解函数是如何工作的,我知道结构是由int,p和b形成的,p将保存表中单元格的数字,但之后我不明白为什么它创建了更多的数组(选项卡1和tab2)以及它如何检查拉丁方= /等,总结,我完全迷失了。

我不是要求逐行解释,这个函数背后的一般概念。 会帮助我很多!

再次感谢< 3

int sudoku(struct sudoku tabla[9][9],int x,int y)
{
int tab[9] = {1,1,1,1,1,1,1,1,1};
        int i,j;
  for(i=0;i<y;++i)
  {
        tab[tabla[x][i].p-1]=0; 

  for(i=0;i<x;++i)
  { 
        tab[tabla[i][y].p-1]=0;
  }
  for(i=(3*(x/3));i<(3*(x/3)+3);++i)
  { 
        for(j=(3*(y/3));j<y;++j)
        {
         tab[tabla[i][j].p-1]=0; 
        }
  }
    int n=0;
   for(i=0;i<9;++i)
   {
    n=n+tab[i];
   }

    int *tab2; 
    tab2=(int*)malloc(sizeof(int)*n);

    j=0; 
    for(i=0;i<9;++i)
    { if(tab[i]==1)
      {
       tab2[j]=i+1;
            j++;
      }
    }
    int ny, nx;
    if(x==8)
    {
        ny=y+1; 
        nx=0;   
    }
    else
    {
        ny=y; 
        nx=x+1;
    }

    while(n>0)
    {
        int los=rand()%n;
        tabla[x][y].p=tab2[los];

        tab2[los]=tab2[n-1]; 

        n--;

        if(x==8 && y==8)
        {
            return 1;
        }

        if (sudoku(tabla,nx,ny)==1)
        {
           return 1;
        } 

    }
    return 0;
}

修改 太好了,我现在了解结构,谢谢李杰的回答。我仍然不理解的是以随机顺序尝试值的部分。我不明白它是如何检查随机值放置是否有效而不调用检查移动是否合法的代码部分,同样,在放置随机数后是否有必要检查网格是否有效? -

3 个答案:

答案 0 :(得分:3)

基本上,函数的调用会填充表(x, y)中{和tabla处的位置,并且函数会假定(x, y)之前的位置“优先”填写,并返回是否可以合法“填写”值。

通过增加x然后y来线性化电路板。

函数的第一部分找出(x, y)合法的值,第二部分以随机顺序尝试值,并尝试通过递归调用填充其余的板。

tab2实际上没有意义,因为tab可以为此目的重用,而且函数会泄漏内存(因为它永远不会free d,但除此之外这些,它有效。)

这对你有意义吗?

修改

检查合法编号的部分中唯一棘手的区域是第三个循环(检查3x3框)。 j的条件为j < y,因为第二个循环已经检查了j == y的值。

<强> EDIT2

我挑剔,但是计算n且用tab2填充合法值的部分应该是

int n = 0;
for (i = 0; i < 9; ++i) if (tab[i]) tab[n++] = i+1;

因此省略tab2的需要(后面的代码只能使用tabn代替tab2)。因此消除了内存泄漏。

修改

请注意,随机性仅适用于有效值(尝试值的顺序是随机的,而不是值本身)。

代码遵循标准的详尽搜索模式:尝试每个可能的候选值,如果搜索成功则立即返回,如果所有候选值都失败则返回失败。

答案 1 :(得分:1)

尝试自己解决数独,你会发现找到解决方案时会有固有的递归。所以,你有自己的功能,直到整板被解决。

至于代码,它可以大大简化,但如果你自己编写代码,它将是最好的。

编辑:

Here是java中的一个,也许它与您尝试的类似。

答案 2 :(得分:1)

原则的简要描述 - 忽略您发布的示例。希望有了这个想法,你可以自己将它与例子联系起来。

基本方法是许多“人工智能”的基础,至少直到80年代末才能看到。许多谜题最常见的解决方案基本上是尝试所有可能的解决方案。

因此,首先尝试所有可能的解决方案,左上角有1,然后是左上角有2的所有可能的解决方案,依此类推。你试图尝试第二个位置,第三个位置等选项。这被称为穷举搜索 - 或“暴力”。

麻烦是它需要永远 - 但你可以缩短很多毫无意义的搜索。

例如,在左上角放置一个1,你就会递归。你将1放在下一个位置并再次递归 - 但现在你发现你违反了两条规则(连续两条规则,3x3区块中有两条规则),即使没有填写其余的规则。所以你“回溯” - 即退出到前一级别的递归并前进到第二个位置。

这避免了批次的搜索,并使事情变得切实可行。如果你跟踪每行,列和块中仍未使用的数字,还有进一步的优化 - 考虑这些集合的交集。

我所描述的实际上是一种求解算法(如果你允许某些单元格已被填充)。生成随机求解的数据是相同的,但是,对于每个数字位置,您必须以随机顺序尝试数字。这也留下了这样的问题:确定哪些细胞留空,同时确保难题仍然可以解决并且(更难)设计具有难度设置的难题。但在某种程度上,这些问题的基本方法已经存在 - 您可以通过运行解决方案算法并查找是否(以及多少)您获得的解决方案来测试一组特定的左空白是否有效,例如,您可以设计搜索一组有效的单元格留空。

困难程度很难,因为它取决于人类对困难的看法。嗯 - 我可以在某处再次适应“困难”...

一种方法 - 设计一种更复杂的搜索算法,该算法使用典型的人类经验法则优先于递归搜索,并且判断难度是所需的最深层次的递归。一些经验法则也可能比其他经验法则更为先进,因此使用它们会更加难以理解。显然困难是主观的,所以没有人能正确回答得分应该如何准确。

这可以为您提供特定难题的难度。直接设计难度级别的拼图将很难 - 但是当尝试不同的单元格选择留空时,您可以尝试多个选项,跟踪所有难度分数,最后选择最接近您的难度分数目标难度级别。