在初看到题目时我是又惊又喜,喜是应该我从小就很喜欢玩数独,惊是因为题目看起来难度不小,但是如果太简单又能学到什么呢?在做作业的过程中我遇到了很多困难,也学到了很多。
遇到的困难及解决方法
- 困难描述
我这次使用的是C#语言,由于以前写算法多是使用C和C++,C#用来写界面,用C写算法对我来说难度不小,有很多函数不懂,很多情况不知道怎么处理,性能也很差。 - 做过哪些尝试
首先我是想到用回溯法生成数独,网络上也有很多人用这种方法,看了一些C++写的,然后想着自己也写写看。 - 是否解决
在写的过程中,我发现自己写的性能很差,但是修改了很久没有改变,我只好暴力求解。 - 有所收获
感觉经过这几天自己特别努力地在学习,学习了各种知识,自己的综合学习能力也有所提升。
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 80 |
· Estimate | · 估计这个任务需要多少时间 | 1000 | 1250 |
Development | 开发 | 800 | 1000 |
· Analysis | · 需求分析 (包括学习新技术) | 100 | 250 |
· Design Spec | · 生成设计文档 | 60 | 50 |
· Design Review | · 设计复审 (和同事审核设计文档) | 40 | 50 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 50 | 50 |
· Design | · 具体设计 | 50 | 50 |
· Coding | · 具体编码 | 100 | 300 |
· Code Review | · 代码复审 | 100 | 100 |
· Test | · 测试(自我测试,修改代码,提交修改) | 100 | 100 |
Reporting | 报告 | 200 | 250 |
· Test Report | · 测试报告 | 100 | 100 |
· Size Measurement | · 计算工作量 | 50 | 50 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 50 | 100 |
合计 | 1060 | 1330 |
解题思路
刚拿到题目的时候,我想到可以用回溯法做,一行一行的填过去,但是想了一下一行一行地填效率是否太低了,又想到要判断没有九宫格都有数字有些麻烦,就想到不如我一个一个九宫格填过去,每次填一个数字,填九次就填完了,同时还不用验证一个九宫格里是否有重复的数字,只要比较行和列就可以了。
设计实现
程序里有两个类,一个是Map,是数独有关的类,里面有三个函数,另一个是Program类,执行程序的类,里面有五个函数。
流程图:
关键代码
static bool Fill(int s, int d, ref Map a,ref int count)//将数字随机填入九宫格(fill the Nine Patch with each number)
{
int i, j, m, n;
Random ran = new Random(Guid.NewGuid().GetHashCode());
for (j = 0; j < 6; j++)
{
i = ran.Next(0, 9);//随机生成一个九宫格中的位置(randomly generate the position to fill in Nine Patch)
m = Yp[s] + yp[i];
n = Xp[s] + xp[i];
if (a.sdk[m, n] == 0 && a.x[n] == 0 && a.y[m] == 0)//判断是否可以填入数字(determine if the number can be filled in)
{
count++;//每次填入时count+1(count + 1 every time you fill in)
a.sdk[m, n] = d;
a.y[m] = 1;
a.x[n] = 1;
a.m[s] = 1;
return true;
}
}
return false;
}
我把数独分成九个九宫格,利用随机数生成九宫格的位置,填入数字,利用一个count,如果反复填入过多,那就跳出循环。
while (a.m[s] == 0)
{
if (!Fill(s, d, ref a,ref count))
{
if (count > 500) return;//count大于500时跳出循环(drop the loop when count>500)
s=Back(s, d, ref a);
continue;
}
}
static int Back(int s, int d, ref Map a)//当一个九宫格填不下去时,回到上一个九宫格(when you can't put the number in this Nine Patch,go back to the previous one)
{
int i, m, n;
if (s == 0) return s;
else s--;
a.m[s] = 0;
for (i = 0; i < 9; i++)
{
m = Yp[s] + yp[i];
n = Xp[s] + xp[i];
if (a.sdk[m, n] == d)
{
a.sdk[m, n] = 0;
a.y[m] = 0;
a.x[n] = 0;
return s;
}
}
return s;
}
当一个九宫格无处可填的时候,回到上一个九宫格再随机填。
测试运行
性能分析
数据:2000
占比最多的函数是填入九宫格Fill函数,最多的是生成随机数填入,我本来想用两个随机数,一个生成填入的x轴坐标,一个生成填入的y轴坐标,但是生成随机数太费时间了,我就生成一个随机数,用数组让一个随机数定位到填入的位置。