• 个人项目实战


    第二次作业

    1.github项目地址:

    https://github.com/chujiuling/jiuling

    2.解题思路描述

    刚开始拿到题目后,看到要求上写着:

    满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。

    第一反应是每填一格就检查这三个条件是否符合,因为第一个格子是固定的,从第二个格子开始填,如果符合就继续填第三个格子,如果不符合,就换一个数填写。

    如果用循环的方法,需要81个for循环嵌套,行不通,所以采用回溯的方法。

    • 从第二个格子开始,根据数独的条件,对比同一行、同一列、同一个九宫格的数字得出第一个未试过的可行解填入,进行下一步运算。(标记该格哪些数字试过)
    • 对剩下的格子进行同样的操作。
    • 遇到无解的情况,进行回溯操作,重复上述运算。
    • 当所有格子都填满,所得结果,即为完整数独棋盘。
    • 直到满足所需棋盘个数,结束。

    除了回溯法,还想过若是把回溯用在中间的九宫格3*3,然后对它运用矩阵转换法,进行行列变换,生成剩下八个单元格,如果第一个格子不是要求的数,再把含第一格的数字的全部格子中的数和含所要求数字的全部格子调换。可是行列变换会重复,得标记,比较复杂,时间有限,所以还是采用回溯法。
    途径:自己思考、百度资料、请教同学。

    3.设计实现过程

    • 需要输入处理,main函数里面进行了设置,使其输入格式为“sudoku.exe -c 生成数独个数”
    • 输出在print函数,当空格填满时输出
    • 填空格的方法(回溯)放在setNum函数中

    4.代码说明:

    //输入格式部分
    if (argc != 3)
    	{
    		cout << "please input two parameters."<< endl;
    		return 0;
    	}
    	if (argv[1][0] != '-' || argv[1][1] != 'c' || argv[1][2] != '')
    	{
    		cout << "please input -c as the first parameter." << endl;
    		return 0;//如果输入第二个参数不是-c 不输出
    	}
    	for (int i = 0; argv[2][i] != ''; i++)
    	{
    		if (argv[2][i] < 48 || argv[2][i]>57)
    		{
    			cout << "please input number as the second parameter." << endl;
    			return 0;//如果输入第三个参数不是数字 不输出
    		}
    		a = argv[2][i] - '0' + a * 10;//a为数独数目
    	}	
    
    //检测三个条件
    for (int i = 0; i < 9; i++)
    		{
    			if ((array1[x][y] == array1[x][i] && i != y) || (array1[x][y] == array1[i][y] && i != x))
    				goto here;
    		}//检查该格所处的同行同列是否重复
    		for (int i = x / 3 * 3; i < x / 3 * 3 + 3; i++)
    			for (int j = y / 3 * 3; j < y / 3 * 3 + 3; j++)
    			{
    				if (array1[i][j] == array1[x][y] && (i != x && j != y))
    					goto here;
    			}//检查该格所处的九宫格内其他格中的数是否重复
    
    //回溯部分
    if (y == 8)
    		{
    			y = 0;
    			x += 1;
    		}//如果y等于8则要换行继续填写
    		else y += 1;//否则右移一格填写
    		setNum(x, y);//填写下一格
    		if (y == 0)
    		{
    			y = 8;
    			x -= 1;
    		}
    		else y -= 1;//回溯法,回到上一层
    	here:
    		array1[x][y] = 0;//试的数不符合三个条件之一时该格就会被置零,尝试填入其他数字   
    
    //输出部分
    void generator::print()
    {
    	if (out.is_open())
    	{
    		for (ii = 0; ii < 9; ii++)
    		{
    			for (jj = 0; jj < 8; jj++)
    			{
    				out << (char)(array1[ii][jj]+48) << ' ';
    				//cout << array1[ii][jj] << " ";
    			}
    			out << (char)(array1[ii][jj] + 48);//输出最后一格
    			//cout << array1[ii][jj];
    			out << endl;
    			//cout << endl;
    		}
    		out << endl;
    		//cout << endl;
    		count1++;//计算有几个数独输出
    	}//9*9格子全部填满,输出
    }
    

    5. 遇到了几个问题:

    怎么知道哪些数字试过了?

    • 为了避免重复,设置了flag数组,记录每个格子对于每个数字是否试过了,比如flag[3][2]=1,代表第第一行左数第四个格子已经试过了2这个数字,如果等于0则是没有试过。
     array1[x][y] = t;
    		flag[x * 9 + y][t] = 1; //标记第x*9+y+1格试过了t数
    		count_t++; //该格试过了count_t个数字
    

    题目要求随机构造出n个数独棋盘。

    • 逐个检测由1试到9,每次输出的数独棋盘不是随机的,比如n=3时,第一次运行得到的三个棋盘,和第二次运行得到的是一样的,因为是按顺序试数生成。因而尝试的数字设置为随机生成,但是每次都随机生成,到最后可能会死循环,故只设置第一个数为随机数,之后尝试的数逐个加一。
    int t = rand() % 10;//生成随机数t
    	for (int count_t = 0; count_t < 9;)//在该格内试着填入t 
    	{
    		if (flag[x * 9 + y][t] == 1)
    		{
    			t++;//试数
    			if (t > 9)
    				t = 1;
    		}
    

    生成txt文件要么为空要么只有一个棋盘。

    • 原本是以只读的形式打开,所以写了就删,改成以写的方式打开,所以文件为空;修改后每次调用函数都重新创建fstream对象out,一直创建,一直覆盖,所以只有一个棋盘。设置第一次填第二格前清空文件,保证刚开始运行文件是空的,接下来指向文件末尾接着写。
    //当时解决方法
    ofstream out;
    	if (temp)			 
    	{
    		if (x == 0 && y == 1)
    		{
    			out.open(".//sudoku.txt", ios::trunc);//第一次填第二格前清空文件
    			temp = false;//避免回溯到第二格清空了
    		}
    	}
    	else                            
    	{
    		out.open(".//sudoku.txt", ios::ate | ios::in);//接下来指向文件末尾接着写
    	}
    

    输出汉字,编译不通过,提示常量中有换行符。

    • 搜索方法:
      I 不用中文 (最后采用英语)
      II 偶数中文 或 结尾加英文的符号,如"."(编译通过,可是输出乱码)
      III 字符转换 GBKToUTF8

    还有各种小问题,就不一一叙述了。

    6.测试运行


    7.性能分析

    <<构建之法>>上讲述了在效能分析时,找到效能瓶颈,进行代码注入的分析,改进,不能盲目优化。对于效能分析的具体过程,还是一知半解,希望在课程中解惑。

    n=1000

    借鉴同学博客,改进有三:

    • 每次调用函数,都重新创建文件流对象,导致大量时间浪费在打开文件上,可把文件流对象放到函数外。
    ofstream out(".//sudoku.txt", ios::out);//输出到txt
    
    • 改为字符输出
     out << (char)(array1[ii][jj]+48) << ' ';
    
    • 输出数独部分从setNum函数中独立出来,成为print函数。
    void generator::print()
    

    改进后:
    n=1000

    8.PSP

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 60 60
    · Estimate · 估计这个任务需要多少时间 60 60
    Development 开发 1200 1440
    · Analysis · 需求分析 (包括学习新技术) 720 600
    · Design Spec · 生成设计文档 60 60
    · Design Review · 设计复审 (和同事审核设计文档) 0 0
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 0 0
    · Design · 具体设计 120 180
    · Coding · 具体编码 150 150
    · Code Review · 代码复审 30 30
    · Test · 测试(自我测试,修改代码,提交修改) 120 180
    Reporting 报告 250 250
    · Test Report · 测试报告 120 200
    · Size Measurement · 计算工作量 30 30
    ·Postmortem& Process Improvement Plan · 事后总结, 并提出过程改进计划 120 120
    合计 1510 1830

    9.心得体会

    没有很具体地去算花了多长时间,从9.4看到作业开始,除了返校,其余时间基本用于摸索这次的作业,笨鸟先飞,不断碰壁,百度资料,在请教中解惑。一步一步走的坎坷,却也走过来了。虽然还存在很多问题,但也解决了不少困惑,也算受益颇多。相对于昨天的自己,有进步!

  • 相关阅读:
    invalid expression: missing ) after argument list in xxx 或者 console.error(("[Vue warn]: " + msg + trace));
    js的alert()
    第9节列表渲染
    第8节条件渲染
    第7节class与style绑定
    CF1215D Ticket Game 博弈论
    CF833A The Meaningless Game 思维
    蚯蚓 队列
    洛谷P2566[SCOI2009]围豆豆
    ants 思维
  • 原文地址:https://www.cnblogs.com/jiuling/p/7500947.html
Copyright © 2020-2023  润新知