第二次作业——个人项目实战
本次作业的项目地址:Github仓库地址
解题思路
嗯,这次的作业要求是
利用程序随机构造出N个已解答的数独棋盘
刚开始看到的时候可以说是非常茫然的,数独以前虽然玩过,但要用程序来构造那就是完全的另一回事了。一般来说,数独的构造是你根据已有的条件,来判断剩下的空格该填什么数。我自己对这个没什么研究,平时玩的时候挺随心所欲的,这个直接导致我在面对如何用程序来解答的时候毫无头绪。何况这次的要求根本就连条件都没有,要自己凭空造。
凭空,还要随机,只有第一位数是指定的,完全没有方向,所以我就先去网上搜了“如何构建数独”,发现不少前人对此都有研究,我在其中找到了两篇对我来说很有用的,如下:
[http://blog.csdn.net/qq_31558353/article/details/50615760]
[http://blog.csdn.net/xiahn1a/article/details/50852849]
第一篇记录了该如何去解一个数独。这次作业对数独棋盘的要求只有一个,就是左上角第一个数字为(学号后两位相加)%9+1,我的学号末两位为16,也就是说我的矩阵左上角那位应为8。
该篇的具体做法大致是说,从第一位开始逐个填数,如果已经有数字了就跳过,如果是空的,就从1开始看能不能填入,判断基准是该位所在的行列和小九宫里是否有跟这个数重复的数,如果没有,就填入这个数,如果有,就跳到下一个数判断能不能填,以此类推。
这种做法很容易遇上某一格一个数都填不进,这个时候就返回到它的前一格,重新走下一个数判断,直到全部填满。
这篇代码的作者非常厉害,仅用了一个n就可以表示数独矩阵每一位的坐标。我因为一开始是用num[i][j]的方法在做,不想改,本身也不能完全照他的走,所以就化用了他的方法。
但即便这样也仅仅是能构建一个以8为头的数独阵,并不能做到随机。这个时候我看到了第二篇博文。这位作者的代码有点繁杂,用到的陌生函数太多我一时看不来,但最开头部分的他的思路给了我非常大的提示。
首先第一行肯定是1~9的一种排列,直接使用shuffle进行随机。
当时看到这句的时候简直是茅塞顿开。数独阵这么多,用第一行随机可不就是能制造大批的不一样的数独嘛!然而很惭愧我不会使用shuffle,而且经过查找发现这个似乎是要在1~9里随机排列。
说到这个就要提老师的那个固定了第一位的要求了,我的首位是8,也就是说剩下8个数得在1~7和9之中随机排列,这就很不一样了,因为我在查“该如何生成一组随机数”的时候,基本上都是要在一个固定范围里随机的,并不能像我希望的那样可以自己指定数字。所以必须要想另外的办法,使我可以随心所欲的用指定的那些数随机排序。
于是我就查找到了第三篇对我来说非常重要的博文!如下:
[http://blog.csdn.net/cxllyg/article/details/7986352]
洗牌算法,我只要自己指定好数组,就可以将[0]位后面的八个数随机排列了。因此我定义了一个数组,内容是{8,1,2,3,4,5,6,7,9},随机的时候将首位除外,剩下的随机排。这也是我第一次认识rand和srand函数。
这些基本就是我的代码的主体构成了。先是在第一个数为8的基础下把1~9随机排序,然后根据已有的第一行推出剩下72个空位应该填什么。
老实说这份作业做得很跌宕起伏,经常是我前一个问题还没理清楚后脚又冒出了另一个问题。命令行输入和查重都是最后一天才发现自己遗漏的,可以说是非常不小心非常不应该的了。何况我最后一天还要返校……命令行还好解决,查重真的就是太困难。一开始做的时候没考虑到,后面想加也很有难度。把全部随机出来的第一排存在同一个数组里,新随机一个就从头开始一个个查有没有重复,数量少还能应付,但多了似乎就会运转不来。之后应该会试着尝试有没有什么更好的方法吧。
设计实现
我一共在头文件里定义了五个函数和一个全局变量,分别是:
bool sign=false; //用以判断数独阵是否完成
extern int num2[1000000][9]; //定义一个用于查重的数组
void firstrank(int num[][9],int n,int sum[][9]); //随机取数独矩阵第一排的值
bool check(int n,int i,int j,int num[][9]); //判定数字n能否放入num[i][j]中
int build(int num[][9],int x,int y); //构建数独矩阵
void change(); //重置sign的值为false
bool diffrent(int a[][9],int b[][9],int n); //比较a,b两个二维数组是否不相同
Main函数在生成一个数独棋盘之前,首先要先运行firstrank,定好第一行数的排列顺序。
然后要运行diffrent来判断是否有重复。每个新生成的一排随机数都要同num2里存放前几轮随机过的数进行比较。一行一行比,每行一遇到不同就跳转下一行继续比较,如果相同,则判断是否是该行最后一个数,如果是,则说明有完全相同的一行,返回false,如果全部比较完都没有重复,那就返回true。
于是就可以开始运行build,从第二行首位开始构建数独。Build中会用到check来判定这个数能否被放进这个位置里,如果check返回的值为true,就下一位;返回的值若为false,就换一个数继续判定。当所有空位都被填上数字时,build里下一个位置就是第十行第一列,这个时候sign会被置为true,build直接return 0,表明数独已经构建完成,可以等待输出。
函数change是为了重复构建函数而备的,因为sign并非是在main函数里所定义,所以为了能够重复使用,在一个数独开始构建前,要先运行change来确保sign的值确实为false。
输出部分因为涉及写入文本文件,所以我直接放在main函数里了,没有另外定义。
输入部分要求命令行输入,还要判断输入的值是否正确,我设定的是输入错误就会提示“输入错误,请重试”。
代码说明
首先是firstrank的第一行随机排序,代码如下
int a[9]={8,1,2,3,4,5,6,7,9}; //定义一个数组,第一个数为我的学号末两位16经计算后得出的固定值8
int ran,temp;
for(int i=2;i<9;i++) //数组除了第一个数固定为8,其余重组
{
ran=rand()%(9-i)+i;
temp=a[i-1];
a[i-1]=a[ran];
a[ran]=temp;
}
check从行,列,小九宫的角度判断是否能够赋值,如下:
for(a=0;a<9;a++) //判断第i行是否有与n重复的数字
{
if(num[i][a]==n) return false;
}
for(b=0;b<9;b++) //判断第j列是否有与n重复的数字
{
if(num[b][j]==n) return false;
}
if(i<3) x=0; //num[i][j]所在的小九宫左上角的行
else if(i>5) x=6;
else x=3;
if(j<3) y=0; //num[i][j]所在的小九宫左上角的列
else if(j>5) y=6;
else y=3;
for(a=x;a<x+3;a++) //判断该九宫中是否有与n重复的数字
{
for(b=y;b<y+3;b++)
{
if(num[a][b]==n) return false;
}
}
build从第二行第一列开始为一个一个空位赋值,全部完成后将sign的值标为true,表示数独构造成功。
if(x==9) //当全部填满时,数独矩阵完成
{
sign=true;
return 0;
}
for(k=1;k<=9;k++) //从1开始逐个测试是否能放入num[x][y]的位置中
{
if(check(k,x,y,num)==true)
{
num[x][y]=k;
if(y<8) build(num,x,y+1); //该位不是这一行的最末位,则继续对其下一位进行置数
else build(num,x+1,0); //该位已经是这一行的最末位,跳至下一行首位进行置数
if(sign==true) return 0; //当数独阵完成时,直接返回
num[x][y]=0; //如果置数失败,则将这位置0,继续寻找下一个适合的数字
}
}
Main函数先要确定随机排序的随机种子和数独最后要输出的文件“sudoku.txt”,然后根据在命令行输入的n来构造数独,如果输入的不属于规定范围内,还要给出错误提示。
n=atoi(argv[argc - 1]); //命令行输入
srand((unsigned)time(NULL)); //随机种子
ofstream outFile; //输出文件“sudoku.txt”
outFile.open("sudoku.txt");
if(n<1000000 && n>1) //判断输入的变量是否为int型
{
for(i=0;i<n;i++)
{
change();
int num1[9][9]={0};
firstrank(num1,i,num2); //确定第一行的排序
if(diffrent(num1,num2,i)==1) //判断是否重复
{
build(num1,1,0); //开始构建数独
for(j=0;j<9;j++) //输出数独阵到文件“sudoku.txt”中
{
for(k=0;k<9;k++)
{
outFile << num1[j][k] << " ";
}
outFile << endl;
}
outFile << endl; //每个数独阵间隔一个空行
}
else i=i-1;
}
}
else
{
cout << "输入错误,请重试。" << endl; //若变量不为int型,则输出错误提示
}
outFile.close();
测试运行
截图如下:
性能分析
执行力,泛泛而谈的理解
执行力,按百科上的说法就是“有效利用资源、保质保量达成目标的能力”,也就是说利用现有的条件和知识,通过自己的力量又快又好的完成需要完成的任务。而泛泛而谈,指浮于表面,没有深入研究,一个人讲出来的东西很肤浅毫无思考力,大概就是泛泛而谈了。
按我个人的理解,拿这次作业作比,如果一个人在看到作业后立刻开始思考入手,然后在deadline来临前从容而高水平的完成了它,那就说明他有很强的执行力了。
泛泛而谈嘛,比如说,我上面提及我要换一种更稳妥的方法来查重复,不用事先设一个那么庞大的数组,我说了,却没有去做,并且在想起时不断强调以后会去做,那就是一种很浅薄的表现。
……老实说,我觉得我现在在这里谈论这些,谈论代码的言辞就挺肤浅挺没有见识的……
遇到的困难及解决方法
关于代码思路之中的困难上面已经提过了,现在说的都是一些细节上但是又很恼人的问题。
一个是命令行输入,今天最后了才发现的,int main(int argc ,char *argv[]),乍一看很莫名其妙,好在我舍友为我倾情指点了一下,可以简单进行使用了。
下一个是输出到txt。这个我查了我的一本讲C++的书,按照书上的说法这个创建的文档一般会跟可执行文件在一起。于是我就照着书上的做法直接用了。我比较喜欢在编代码的时候一个一个功能加,所以在建txt之前我都是用cout直接进行输出的。先前的时候是另设了一个output函数来输出,后来为了用outFile又改成直接放在main函数里。
最困难的还是没有办法查重复。我如果要改的话前面firstrank()就要跟着变,时间不足,所以就直接建数组。数组的话必须视线声明大小,[1000000][9]太大了,我一开始在main函数里建的,后来发现没办法执行,又改成全局变量。这下不会报错了,但我自己试了下,我的电脑最多只能随机出5000个左右的数独阵,如果输入6000,就会一直停留在运行,不能结束。我猜测是电脑容量的问题。
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 30 |
· Estimate | · 估计这个任务需要多少时间 | 10 | 30 |
Development | 开发 | 620 | 890 |
· Analysis | · 需求分析 (包括学习新技术) | 60 | 50 |
· Design Spec | · 生成设计文档 | 0 | 0 |
· Design Review | · 设计复审 (和同事审核设计文档) | 0 | 0 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 0 |
· Design | · 具体设计 | 30 | 60 |
· Coding | · 具体编码 | 240 | 300 |
· Code Review | · 代码复审 | 30 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 240 | 420 |
Reporting | 报告 | 90 | 90 |
· Test Report | · 测试报告 | 60 | 70 |
· Size Measurement | · 计算工作量 | 10 | 5 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 15 |
合计 | 870 | 1010 |
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· 卧槽!C 语言宏定义原来可以玩出这些花样?高手必看!
· langchain0.3教程:从0到1打造一个智能聊天机器人