1)GitHub项目地址
GitHub地址
使用语言:c
运行环境:WIN10
开发平台:Visual Studio Professional 2015
2)预计程序耗时
3)解题思路描述
当看到9*9数独题目时,一开始觉得并不难。我首先想到的是做一个3*3的数独,因为我认为可以以小见大,原本是想通过循环实现,但是后面发现还是递归比较靠谱。在做数独的过程中,我百度了很多资料:明白了使用rand前要用srand设置函数,并且因为是要实现多个数独,速度太快,srand会设置出相同的种子;搞清楚了数独的基本做法,要先对行列进行比较,还要比较所在的小九宫格,当试值失败时,还要将值设回初值不影响接下来的测试;也搞清楚了一些被遗忘的基础知识,比如对文件的读写,和防止数据写入的置换等等。虽然自己的基础不稳,但是还是在不停的百度的艰辛过程中完成了此次作业。
4)设计实现
我用一个二维数组来存放所形成的数独,通过判别行列不重复,所在九宫格重复来决定当前位置的取值。当完成某个位置的填值时,递归继续下一个位置或者下一行的第一个位置的填值。当填值失败时,当前位置设置回初值,并返回上一层。
用到了如下几个函数:
- void main() 程序的入口, 在此接受所需的数独个数并且进行输入合法判断,后调用其他函数。
- void generate(int num) 随机生成数独第一行数据的函数。
- bool generateall(int m, int n) 递归函数,在此函数递归完成数独。
- void show() 输出函数,在此将所形成数独输出,并且将其写入棋盘文件
5)代码说明
bool generateall(int m, int n)
{
if (m > 8 || n> 8)//此时数独已完成
return true;
int i, j, k;
for (k = 1; k <= 9; k++)
{
bool flag = true;//flag标记k能否可以放在此位置
for (i = 0; i<m; i++)
{ //查询此列是否有相同的数
if (grid[i][n] == k)
{
flag = false;
break;
}
}
if (flag)
{
for (j = 0; j <n; j++)
{//查询此行是否有相同的数
if (grid[m][j] == k)
{
flag = false;
break;
}
}
}
if (flag)
{
//查询所在九宫格是否有相同的数
int p, q, p1, q1;
p = m - m % 3;p1 = p + 3; //所在九宫格的行上下限
q = n - n % 3;q1 = q + 3;//所在九宫格的列上下限
for (i = p; i <p1; i++)
{
for (j = q; j <q1; j++)
{
if (grid[i][j] == k)
{
flag = false;
break;
}
}
}
}
if (flag)
{
grid[m][n] = k;
if (n < 8)
{
if (generateall(m, n + 1)) return true;//到同行的下一列
}
else
{
if (m < 8)
{
if (generateall(m + 1, 0))//到下一行的第一列
return true;
}
else return true;//此时数独已完成
}
grid[m][n] = 0;//当数字1到9都测试失败时,恢复原值
}
}
return false;
}
void generate(int num)
{
int index = 0, sum = 36, i, j;
srand(num);//设置种子
for (i = 0; i < 9; i++)
for (j = 0; j < 9; j++)
{
grid[i][j] = 0;//初始化矩阵
}
for (int i = 0; i < 8; ++i)//随机产生第一行
{
index = rand() % 9;//产生随机数
while (grid[0][index] != 0)
{
index = rand() % 9;
}
grid[0][index] = i + 1;
sum -= index;
}
grid[0][sum] = 9;
}
void show()
{
int i, j;//输出生成数独
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
printf("%d", grid[i][j]);
}
printf("
");
}
FILE *f; //将生成数独写入文件
f = fopen("shudu.txt","ab+");
if (f == NULL)
{
printf("文件打开失败!
");
return;
}
else
{
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
fprintf(f, "%d", grid[i][j]);
}
fprintf(f,"
");
}
fprintf(f, "
");
}
fclose(f);
}
void main()
{
char a[11] = { 0 },b;
int flag = true,i,k=0;
scanf("%s",&a);
for ( i = 0; a[i]!=0; i++)
{
if (a[i]< 48 || a[i] > 57)
{
printf("输入有错!"); flag = false; break;
}
else { k = k * 10 + (a[i]-48);
}
}
if (flag)
{
while (k)
{
printf("
");
generate(k);
generateall(1, 0);
show();
k--;
}
}
system("pause");
}
6)测试运行
7)性能分析
当测试数据为4000时:
如报表所示:因为本身将4000个数独写入文件的同时输出到屏幕,所以实现此功能的show函数占用的cpu很大;因为耗时比单单写入文件久,这一点由printf所占用的cpu就可以看出;
于是将输出到屏幕的代码注释,输入值还是4000时,得出如下报表:
可以看出时间上的明显缩短,由1:14分钟缩短为7.257秒,并且大部分函数的总CPU都有了大幅度的下降。
自己实现的这个代码是采用递归,本质上是采用最简单的暴力破解,但是能力有限,没有办法采用较好的方法。
8)实际程序耗时
9)备注
因为代码中使用了fopen,所以vs中必须先进行设置,如下