我的Github项目地址:
[https://github.com/SinRoyal/Sudoku]
关于执行力、泛泛而谈的理解
执行力
- 首先谈一下关于执行力的理解,在我看来,我觉得比较通俗的理解是执行并完成任务的能力,但是,在理论上它的意义则不是这么简单,它指的是贯彻战略意图,完成预定目标的操作能力。我认为是否有强大的执行力是一个人是否能取得成功的关键之一。一个人如果有强大的执行力,首先说明他的意志很坚定,能持之以恒地为了自己理想的目标去完成任务;其次,他在做事的时候一定是有自主自发性的,而且具有着强烈的责任心;还有,他对于学习和工作也具有积极向上的热情,以及能够克服困难的韧性。这样才能圆满完成任务,达到自己的理想目标,也就是执行力的体现。
- 在阅读了上次的作业的评分总结后,我的感触很深,我认为助教老师提的那些问题都发人深省,尤其是定下一个可衡量的具体的目标以及分配时间以后,是想想就算了,还是真的有做到实处?如果没有做到,这就是光说不练假把式的体现吧。而且,在分配的具体时间上,如果有什么意外发生打乱了你原来的计划,或者是你的时间预估出现错误,导致任务没有完成,目标没有达到,那该怎么办?因此,我觉得评分总结中的一句话挺有道理的:有一点空闲时间我们就把事情往前推进哪怕只有一点点。
- 最后,我认为,我们一定要提高自己的执行力,这对于以后的学习和工作真的很重要。举个实际例子:大二下学期的数据库应用实践这门课,我一开始的定下的目标是在两个星期内完成,这两个星期内平均每天拿出1到2个小时去做这个作业,使用的语言是JAVA,我也说到做到,每天都有做一部分,结果在做到一半的时候出现了意外,我卡住了做不下去了,打乱了计划。于是临时决定改用php去做,也有制订好计划,但与之前不同的是,由于对php较为陌生,所以要先自学,并且时间也略微有些紧迫,所以我每天只要一有空闲时间就会去自学php和html的知识,同时边学边做,不断添加内容,最后在deadline几天前也完成了任务,我想这也体现了我的执行力的提高吧。
泛泛而谈
- 关于泛泛而谈的理解,我想我的看法与评分总结中的类似,就是说空话、喊口号等表现,尤其是评分总结中助教老师提出的几个泛泛而谈的关键词:非常,一点,很多等等。我想到我以前制定目标时也是这样,例如:做很多题目,看很多书,做一点运动,敲一点代码等。这些都是泛泛而谈,没有制定详细的,可实施的计划。这是不可取的,所以要避免说空话这种现象,而要有理有据,并且做到实处。
- 举个例子:在以后要复习考研的那段日子中,当我给自己制定学习计划时,就不能有类似这样的目标:我每天都要花很多时间去复习,要做很多题目,要背很多英语单词等等。而应该是我每天具体要花多少个小时去复习,在这些时间中,你哪个时间段要看什么读什么背什么,以及你一天要做的题目有多少题,这样才能提高学习效率,最终完成每天的学习任务,也避免了泛泛而谈的不良后果。
解题思路描述
- 在查阅一些资料以及在纸上自己模拟了填数的情况后,我想到的方法是先让第一行生成随机的9个数排成一列,填数从第二行第1个开始,填入后根据数独的规则进行判断。如果符合规则,就对下一格进行同样的判断,顺序是先从左到右,再从上到下。那一旦有某一格对于任何数字的填入都不符合数独的规则,就要进行回溯,重新填上一格的数字,这样一直迭代下去,直接找到一个解输出。
- 我想到的一个难点是在于如何用代码写出合适的符合数独规则的判断函数,因为根据数独规则,每一行,每一列,每一个小九宫格中,9个数都不能有重复的,必须为1~9的随机分布。行和列较为容易判断,小九宫格会稍微难一些,可能需要用到数学知识,去推导坐标公式。
- 另外还有一个难点,就是回溯法的应用,这个要用到的算法知识是递归,因为它是一个点迭代一个点,有问题则返回上一个。而递归是我一直不太会的算法,需要好好研究一下。
设计实现过程
- 我这次准备使用二维数组来模拟数独棋盘,同时设计两个函数,一个是判断函数,一个是填数函数,二者类型皆为布尔型,填数函数需要调用判断函数,而主函数调用填数函数。
- 需要用到的头文件有string(定义字符串类变量所需)、stdlib.h(调用rand(),srand()函数所需)、time.h(使用当前时钟作为随机数种子时所需)、fstream(输出结果到文件中所需)、algorithm(使用STL库中函数所需)。
- 其中最关键的函数就是填数函数,编写它的思路会在下面的代码说明中解释。
关键代码展示及说明
//判断是否符合数独条件
bool judge(int i,int j,int number)
{
int k;
for (k=0;k<j;k++)//判断行
if (a[i][k]==number)
return false;
for (k=0;k<i;k++)//判断列
if (a[k][j]==number)
return false;
int count=j%3+i%3*3;
while (count--!=0)
if (a[i-i%3+count/3][j-j%3+count%3]-number==0)//判断小九宫格
return false;
return true;
}
- 判断函数
- i,j分别为横坐标和纵坐标
- 判断行与列是否符合数独规则较为简单,只要用循环,将某一行和某一列检查一遍过去就行。
- 判断小九宫格较为复杂,需要由九宫格的规律推导公式,我想到的简洁的方法是可以把后6个小九宫格都看成是最上面的3个小九宫格,方便推导。首先是count,它指的是在某个九宫格里面某个格子在填之前它前面已经填过的数的个数,那么在一个小九宫格内,count的值是有规律的,不同的值都跟3有着关联,以及行数列数,行数的范围是18,因为是从第二行开始填,而列数的范围是08,那如果想把后面的九宫格都看成是上面的,则都需要对3取余,然后行数还得乘上3,因为在计算count的时候,一整行可以直接算进去,count的组成都是0~2个满行加上剩下的不够3个的那一行,所以行数乘3,推导出公式count=j%3+i%3*3。
- 坐标的公式也可以推导,检查九宫格内的已填的数的过程是相反的,顺序是从右至左,从下至上,首先是行坐标公式的推导,无论是哪一行,它的上方总会有0个或者1个或者2个完整的九宫格,而满九宫格必定有3行,要算上这些部分,所以i-i%3,那随着count次数的减少,如何判断到了哪一行呢,那就要根据count的范围了,count可以分为3个范围:13,46,78,3个范围代表此时检查的点所在的行位置,所以用count/3就可推出在本九宫格内的行数,因为(13,46,78)/3的结果是0,1,2;对于列坐标的公式,推导出j-j%3与i-i%3的原理相同,但是,要注意,在某一个九宫格内列的位置是固定的,就只能是0,1,2,所以用count%3就可推出在本九宫格内的列数。最终推导出来的公式为:i-i%3+count/3和j-j%3+count%3。
bool sudoku(int i,int j)
{
if (i==9)
return true;
int no=rand()%9;
int m;
for (m=0;m<9;m++)
{
int p=(no+m)%9+1;//产生随机尝试序列,序列有一定的顺序
if (judge(i, j, p))
{
a[i][j]=p;
int i_=i;
int j_=j+1;
if (j_==9)//如果一行填满了,则开始下一行
i_++, j_=0;
if (sudoku(i_, j_))//递归
return true;
}
}
return false;//某一个点不符合条件就回溯
}
- 填数函数
- i_和j_代表的是某个填完的点的下一个点。
- 首先,如果填满了8行,则代表找到了一个解,因此返回true。
- 接下来,我想产生一个用来尝试填数的随机序列,调用了rand()函数,并且让这样的序列有顺序。
- 然后,这个函数核心的原理就是填一个点时,调用judge函数判断,符合规则就赋值,这样就填完了一个点,填下一个点,再自身调用填数函数,再判断是否可以填数,然后下一点又包含再下一个点,这样一直递归下去,直到所有的点都填完,产生一个解。
- 如果在填某个点时,judge函数判断出来不符合规则,就不能填,并且要返回上一个点,因此返回false。
int main(int argc,char **argv)//main函数参数
{
ofstream fout("sudoku.txt",ios_base::trunc);//以文件形式输出,并且方式为覆盖
srand(time(0));//设置随机数种子
std::string str;
int len,flag=0;
int k;
int i,j,temp;
int n=0,m;
str=argv[2];
len=str.length();
for(k=0;k<len;k++)
{
if(str[k]<'0'||str[k]>'9')
{
cout<<"输入有误,请重新输入。"<<endl;//输入的只能是数字
flag=1;//标记
break;
}
}
if(flag==0)
{
n=atoi(str.c_str());
}
if(n>1000000)
{
cout<<"输入的数字超出范围,请重新输入"<<endl;//N的范围为0~1000000
}
for(m=0;m<n;m++)
{
for(i=0;i<9;i++)
{
a[0][i]=i+1;//给第一行赋值1~9
}
temp=a[0][0];//交换
a[0][0]=a[0][2];
a[0][2]=temp;
random_shuffle(a[0]+1,a[0]+9);//产生第一行的随机数,并且第一个数被定为我的学号后两位相加整除9加上1,只有后八个随机
sudoku(1,0);//填数从第二行第一个开始
- 主函数中关键代码
- n为生成棋盘的个数,它由string类型转换成int类型。
- 实现本次作业新增要求的过程如下:在a[0][0]a[0][8]均按顺序赋值19后,我的想法是,交换第一个数和第三个数,让第一个数始终为3,再让后八个数去进行随机,调用的是random_shuffle函数。
- 填数函数的一开始的实参设为1和0,因为填数是从第二行第一个开始。
测试运行结果
- 第一次测试在命令行中输入:sudoku.exe -c abc,命令行中产生报错信息:输入有误,请重新输入。
- 第二次测试在命令行中输入:sudoku.exe -c 1,在同一目录下生成sudoku.txt,文件中的内容为一个已解答的数独棋盘。
- 第三次测试在命令行中输入:sudoku.exe -c 3,sudoku.txt中的内容为三个完全不同的已解答的数独棋盘,除了第一个数均为3以外,其他各不相同。
遇到的困难及解决方法
- 困难描述
- 写judge函数的时候,在判断小九宫格部分,不知道该怎么做。
- 写sudoku函数的时候,由于不太理解递归的原理,不知道怎么编写。
- 在完成新要求时,由于对STL库中random_shuffle函数的用法不太了解,所以不知道如何让第一个数保持不变,而让后八个数去随机。
- 在完成程序功能后,我发现我的输出文件内容有问题,因为每次运行程序输出的结果都是紧接在之前运行程序时输出的结果后面,由于不清楚对于输出结果的要求,便请教助教老师之后后了解到要求是每次都要覆盖上次的执行结果,但是不知道如何怎么做才能让输出方式为覆盖,而不是承接。
- 做过哪些尝试
- 亲自动手在纸上画出多个九宫格,研究九宫格的原理,同时假设了很多例子,寻找它们之间的规律。
- 在网上搜索一些经典递归算法来看,理解递归的思想,也有去请教一些算法方面比较强的同学。
- 在网上搜索random_shuffle函数的用法,并尝试了一下。
- 在网上查找解决方法,最终找到了,即ios_base::trunc。
- 是否解决
- 有何收获
- 算法跟数学还是有很大的关系,有时候研究算法还是需要动笔去写,去推导,才能得出正解。
- 对于递归算法有了更好的理解。
- 了解到random_shuffle函数中的参数是迭代器,是要把数组的地址传进去。于是通过改变下标,解决这个问题。
- 了解到了c++中文件输入和输出的知识。
关于改进
- 在改进程序性能上所花费的时间为30分钟。
- 改进思路:将程序结果输出的方式由直接在控制台输出改为输出到文件中。
- 性能分析图(测试数据为10000)
- 调用次数最多的函数
PSP表格
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
60 |
50 |
· Estimate |
· 估计这个任务需要多少时间 |
60 |
50 |
Development |
开发 |
940 |
1020 |
· Analysis |
· 需求分析 (包括学习新技术) |
240 |
220 |
· Design Spec |
· 生成设计文档 |
30 |
30 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
30 |
20 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
40 |
30 |
· Design |
· 具体设计 |
60 |
90 |
· Coding |
· 具体编码 |
180 |
240 |
· Code Review |
· 代码复审 |
60 |
30 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
300 |
360 |
Reporting |
报告 |
160 |
180 |
· Test Report |
· 测试报告 |
60 |
60 |
· Size Measurement |
· 计算工作量 |
40 |
30 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
60 |
90 |
合计 |
|
1160 |
1250 |
学习进度条
第N天 |
新增代码(行) |
累计代码(行) |
本日学习耗时(分钟) |
累计学习耗时(分钟) |
重要成长 |
1 |
14 |
14 |
60 |
60 |
通过演算,推导出了公式,提高了数学能力,写出了判断函数 |
2 |
22 |
36 |
60 |
120 |
了解了递归算法的原理,写出了填数函数 |
3 |
22 |
58 |
120 |
240 |
熟悉了STL库中random_shuffle函数的用法,了解了c++文件输入输出的知识 |
4 |
|
|
90 |
330 |
学习了Git和Github的网上教程,知道了如何使用 |
5 |
13· |
71 |
30 |
30 |
知道了c++如何让string类型转换为int类型的方法,以及了解了带参数的main函数的原理 |