| 摘 要 以2006年首届世界数独大赛决赛题目为例,说明用C语言模拟数独的人工解题思路。介绍了唯一法、排除法及尝试法三种解题方法。用C语言模拟数独的人工解题思路,有利于学习C语言及相应的编程技巧,尝试着把人的思维方式转化成相应的计算机程序,便于人工智能方面的研究。试验验证了程序的正确性。 关键词 数独; C语言; 人工解题; 候选数     一、引言 数独是一种源自18世纪末的瑞士,后在美国发展、并在日本得以发扬光大的数学智力拼图游戏。数独的玩法逻辑简单,数字排列方式千变万化。不少教育者认为数独是锻炼脑筋的好方法。数独游戏成功的原因除了它的趣味性和挑战性之外,更重要的原因其实是因为它很容易上手。玩这个游戏只需要认识1~9这九个数字,然后再需要一定的逻辑推理加上必要的耐心就可以了。 数独游戏的规则是:在一个9x9的大正方形中,每一行和每一列都必须填入1至9 的数字,不能重复也不能少;在每个由粗线隔开的3 x 3小九宫格(也被称为“区”)中,也必须填入1至9的数字,同样不能重复也不能少;每个数独游戏只有一个解答方案。一般而言,一条数独题会给出1/3左右的数字作为初始条件,剩下的2/3空白处由读者完成。简单的数独题目初始时会有更多的数字。 用C语言模拟数独的人工解题思路,有利于学习C语言及相应的编程技巧,不仅可以找寻计算机程序与人思维方式的差异,而且可以尝试着把人的思维方式转化成相应的计算机程序,便于今后人工智能方面的研究。   二、数独解题思路 数独游戏上手简单,但是随着难度的加大将会充满挑战。数独的解题方法多种多样,归纳起来主要有三种方法:唯一法、排除法和尝试法。 唯一法指的是在任何一行、一列或一区中,如果仅有一个数字没有填上,那么这个数字一定是目前在这一行、这一列或是这一区里应该填上的那个数字。排除法指的是假如在某一行中的两个空格拥有相同的两个候选数字,虽然不能确定哪一个数字应该放在哪一个空格中,但至少可以确定,这一行中没有任何其他的空格可以放入这两个数字。这个原则同样适用于列和区。尝试法指的是首先选定一个候选数最少的空格,再选择其中较小的一个候选数字,假定这个候选数字就是最终的答案,然后根据往下判定其它空格,如果最终解决问题当然万事大吉;如果发现在某一相同的行、列或区中一定会出现相同的两个数字,那么可以断定前面的假设是错误的;这个时候,要重新选择另一个候选数字进行尝试,直到解题。 当然唯一法、排除法及尝试法还可以扩展,这些在用C语言实现时将做进一步探讨。从解题的顺序来看,优先使用唯一法。在唯一法无法实现解题的前提下,再使用排除法。一般入门级、中级、高级,甚至某些黄金级(骨灰级)的题目用这两种方法是可以解题的。如果在前两者循环使用后,仍然不能解决问题,再使用尝试法。高级或黄金级(骨灰级)的题目通常要用到尝试法,有些难题甚至要多次尝试,对许多空格都要尝试。如果尝试过多,人工解题就失去了太多的乐趣。通常对某一个空格使用尝试法之后,再结合唯一法、排除法可以顺利解题。   三、C语言程序 1.唯一法 这里以2006年首届世界数独大赛决赛题目为例来说明用C语言模拟数独的人工解题思路。此题用C语言描述如下: int a[x][x]={ 0,0,0,7,5,0,0,0,0, 0,3,0,0,4,8,0,2,0, 1,0,0,0,0,0,0,0,6, 0,4,0,0,0,0,0,0,8, 7,9,0,0,0,0,0,3,1, 2,0,0,0,0,0,0,7,0, 5,0,0,0,0,0,0,0,7, 0,8,0,3,2,0,0,4,0, 0,0,0,0,6,9,0,0,0               }; 其中x为9,0代表空格。这是一个二维数组,按C语言来定行列,如第1行的7其行列为[0][3]。人工解题首先考虑的是哪一个空格只有唯一解,由本题可见,a[4][4]只能填8,因为其行、列或区中已经有了除8以外其它1-9的8个数据;写入8之后,接下来可在a[6][4]写入1、a[6][5]写入4、a[6][3]写入8、a[8][3]写入5、a[7][5]写入7。  人可以一眼看出象这样的唯一解,但计算机不行,必须用相应的程序来实现此功能。首先需要对行中的整数0进行计数,记录其相应位置。然后寻找每一行、每一列、每一区中缺少的整数。不提倡用随机数来生成数据,一来没有必要,二来用循环及判断很容易找到缺少的数据。最后找出每个空格可写入的整数(即候选数)与每个空格总共可写入几个整数(即个数)。找候选数及其个数的程序较难,以此为例略加说明:     for (r=0; r<x; r++)     for (h=0; h<rowcnt[r]; h++)                   {                 n=r/3;                          t=wei[r][h];                 m=t/3+3*n;           if(b[r][t]==0)          {             for (j=0; j<colcnt[t]; j++)               {                 for (i=0; i<rowcnt[r]; i++)                    {        if (roww[r][i]>0 && roww[r][i]==colw[t][j])   //寻找相同的整数 //首先是每行缺少的整数与列中的第1个数比较 //然后是列中的每行数据依次被比较 //再是wei的位置进行移动 //最后是换到下一行                     {                     aflag = 1;                     break;  //跳出i                     }                     else                         aflag =0;                 }//--end for i             if (aflag ==1)             {                 for (q=0; q<newcnt[m]; q++)                   { if (neww[m][q]==roww[r][i])                     {                       xian[r][h][rowk]=roww[r][i];                         rowk++;                         break;//跳出q                     }                 }                        }  //end if (aflag ==1)                 aflag=0;               }   // end for j                             }//end if(b[r][t]==0)     samecnt[r][h] = rowk;      rowk = 0;     }   // end for h |