zoukankan      html  css  js  c++  java
  • 软件工程第二次作业

    0.相关链接

    题目链接地址
    github链接地址

    1.解题思路

    • 胡思乱想
      • 大致思路有两条,一个是先生成数独终盘,再矩阵转换,(感觉会比较有限,不考虑),二个是随机填数,可以按照数字填(将1填完,再填2这样),按行/列填,按九宫格填
    • 难度瓶颈
      • 考虑随机填数的思路,难点就是填数要随机,但是同行,同列,同格都有限制,自由又很不自由,一开始打算按照数字填,因为觉得数独,1只跟1有冲突,与其他数字,不在一个位置就好了。但是这样满盘随机放数字,就不知道怎么代码实现。
    • 最终选择
      • 网上看了随机的思路,最后选择按行填数,这里参考silenpy的数独终盘生成算法(java)很暴力,但是很容易实现。相同点:第一行数字无限制,1-9随机放,第二行到第九行,一个一个数字填,每次用SET存1-9,然后将同行,同列,同格数字踢出去,(可能将1-9全部踢出去了)剩余数字随机选择。这样保证当前填数正确。不同点:对无数可选情况的处理,我考虑整行重新填数,但是存在整行数字无论怎么填,都不可能正确的情况。解决方法:设置一个整行重新生成的最大限值,超过此值,整个数独重新生成。
    • 改进版本
      • 最后测试的时候,发现上个思路先判断再放数字太慢,SET会重复插入,删除,而且随机选数必须迭代。改进是先放数字,再判断是否正确,这里参考Swing数独游戏(二):终盘生成之随机法,PS:已经确定一位同学吐槽这个算法,一位同学貌似是吐槽这个算法,但是算法是真的,思路与算法有出入,我也被坑了。

    2.设计实现

    只是生成数独终盘,不考虑附加作业,就没有考虑类,只是函数。

    • 版本一:
      getShudu首行调用getfirst,剩余72个空格调用getone,最后调用print输出

    • 版本二:
      getshudu首行调用creatArray,首行数字直接等于随机生成的数组数字,第二行到第九行,依旧调用creatArray,但是不可直接填入,checkbtnArray用于判断随机生成一组数字是否可用,for循环从0到8,当前行一个一个填数,checkone判断当前位置能否从数组中取得合法数字,如果有一个位置无法取得合法数字,则此随机数组不可用。需要再次随机生成,最后调用print打印。

    3.代码说明

    • 版本一:

      • 对首个数字处理,只需要在getfirst先将7从集合移除,放入第一个数字置,剩余数字再随机
      Array[0][0] = 7;
      basic.erase(7);//basic为集合名称
      
      • getone踢出同行/列/格数字后,如果集合中仍有数字,随机选择一个,如果没有数字,看当前getone调用次数times是否超过最大限制,如果没有,此行重新生成,如果超过,不做处理,getshudu会进行处理
      if (basic.size() == 0) {//无数可选
      	
      		if(times <= MAX_Time){
      			if(col/3==0){
      				for(int k=0;k<3;k++){
      					name[0].insert(Array[col][k]);
      				}
      			}
      			for (int k = 0; k <= row; k++) {
      				getone(col, k);//此行重新生成
      			}
      		}
      	}else {
      		int num = rand() % basic.size();
      		set<int>::iterator it;//定义前向迭代器?
      		int j;
      
      		for (it = basic.begin(), j = 0; it != basic.end(); it++, j++) {
      			if (j == num) {
      				Array[col][row] = *it;
      				//basic.erase(Array[0][i]);
      				break;
      			}
      		}
      
      	}
      
    • 版本二:

      • 随机生成数组,对首个数字的处理,数组btnArray,初始化放入1-9,将1与7交换,然后循环产生随机数字(1-8),将该位置数字与btnArray[1]交换,得到一组随机数字
      void creatArray() {
          times++;
          for (int i = 0; i<9; i++) {
              btnArray[i] = i + 1;
          }
          btnArray[0] = 7;
          btnArray[6] = 1;
          for (int i = 0; i<20; i++) {
              int t = rand() % 8 + 1;
              int temp = btnArray[1];
              btnArray[1] = btnArray[t];
              btnArray[t] = temp;
          }
      }
      
      • checkbtnArray
       bool checkbtnArray(int row){
      	int judge;
      	for(int i = 0 ;i < 9;i++){
      		judge = 0;
      		for(int j = 0;j < 9;j++){
      			Array[row][i]= btnArray[j];
      			if(checkone(row,i)){//如果数字合适,填入,换下个位置
      				judge = 1;
      				break;
      			}
      		}
              //数组数字选遍,仍无合适数字,则此数组不可用
      		if(judge == 0){
      			for(int k = 0;k<=i;k++){
      				Array[row][k]=0;
      			}
      		    return false;
      	    }
      					
          } 
          return true;
      }
      

    4.测试运行

    • 命令行输入:

    • 生成文件部分截图

    5.效能分析以及改进

    仅针对版本二,版本一无力回天了。

    • 第一次改进:100万当时运行了15分钟,发现约50%时间耗费在输出上,(忘记截图,后续补上)原本输出函数
    char* path = "./sudoku.txt";	// 创建文件的相对路径
    ofstream fout(path);
    void print(){
    	for (int i = 0; i < 9; i++) {
    		for (int j = 0; j < 9; j++) {
    			fout << Array[i][j] << " ";
    		}
    		fout << endl;
    	}
    	fout << endl;
    }
    

    看了大佬代码,用的putchar,果断换,也用printf试过,后者慢一些

    freopen("./sudoku.txt", "w", stdout); 
    void print() {
    	for (int i = 0; i < 9; i++) {
    		for (int j = 0; j < 9; j++) {
    			char num = '0'+Array[i][j];
    			putchar(num);putchar(' '); 
    		}
    		puts("");
    	}
    	puts("");
    }
    

    运行截图,100万数据:七分多钟

    printf函数所占比例:

    • 第二次改进,一次运行后,print函数消耗时间大大减少,但是checkone,与checkbtnArray函数比重增大。
      查看代码发现:数组里面的数字如果合适,会被填入,但是下次依旧会再次被检验。
    for(int j = 0;j < 9;j++){
    	Array[col][i]= btnArray[j];
    	if(checkone(col,i)){
    		judge = 1;
    		break;
    	}
    }
    

    将已经正确填入的数字置0,加以判断

    for (int j = 0; j < 9; j++) {
    	if(btnArray[j]!=0){
    		Array[col][i] = btnArray[j];
    		if (checkone(col, i)) {
    			judge = 1;
    			btnArray[j] = 0;//表示已经使用过 
    			break;
    		}
    	} 	
    }
    

    这样也保证同行不会有重复,checkone注释掉同行检查

    /*
    	//同行不可重复 
    	for (int i = 0; i < row; i++) {
    		if (Array[col][row] == Array[col][i])return false;
    	}
    	*/
    
    	//同格不可重复 
    	for (int i = (col / 3) * 3; i < col; i++) {
    		for (int j = (row / 3) * 3; j < (row / 3) * 3 + 3; j++) {
    			if (Array[col][row] == Array[i][j])return false;
    		}
    	}
    
    	//同列不可重复 
    	for (int i = 0; i < (col / 3) * 3; i++) {
    		if (Array[col][row] == Array[i][row])return false;
    	}
    

    运行截图,100万数据,跑了六分多,快了一分多,checkone与checkbtnArray占比有所下降,但是依旧很大,其中消耗最大的函数是checkone

    • 意外发现,本来构建之法上面写了,使用效能分析工具确保编译的程序是Release版本,但是当时不知道release是什么,后面发现Debug可以切换成Release,又跑了一次100万,三分多,不去想大佬程序耗时,就知足了。

    6.PSP

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 10 20
    · Estimate · 估计这个任务需要多少时间 10 20
    Development 开发 630 960
    · Analysis · 需求分析 (包括学习新技术) 150 200
    · Design Spec · 生成设计文档 10 0
    · Design Review · 设计复审 (和同事审核设计文档) 30 10
    · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 20 10
    · Design · 具体设计 120 120
    · Coding · 具体编码 180 240
    · Code Review · 代码复审 60 300
    · Test · 测试(自我测试,修改代码,提交修改) 60 80
    Reporting 报告 40 70
    · Test Report · 测试报告 20 60
    · Size Measurement · 计算工作量 20 10
    · Postmortem & Process Improvement Plan · 事后总结,并提出过程改进计划 120 180
    合计 680 1050
  • 相关阅读:
    MyEclipse中配置Hibernate
    struts2_对Map进行双层迭代
    Hibernate关联关系全集
    CodeIgniter+Smarty配置
    去掉php框架CI默认url中的index.php【整理】
    jquery的show方法是display:block还是display:inline呢?
    Codeigniter中的Error【转】
    去除 inlineblock 空隙终极解决方案
    jquery三级折叠菜单
    css实现页面文字不换行、自动换行、强制换行
  • 原文地址:https://www.cnblogs.com/liu424/p/7502217.html
Copyright © 2011-2022 走看看