GitHub:sudoku

解题思路描述

刚看到题目的时候,我去,好难。吃了根冰棍冷静下来,开始细细思考。题目的要求是随机生成N个不重复的数独棋盘,有两种方案:1.用数字1~9填满第一个九宫格,然后再去填下一个九宫格,直到九个九宫格都填满,并且不会每一行每一列不会有相同的数字。2.用数字1填第一个九宫格,然后再填第二个.......直到九个九宫格都填入了1,再把数字变成2,再一个个地去填九宫格,以此类推,直到9个数字都填入。我认为第2种方案实现起来会更容易一些,因此决定采用第二种方案。
接下来便是确定数据结构和方法了。如果单纯采用二维数组来实现递归的话,我不知道该如何表示已经遍历过的格子。因此我决定采用链表加上二维数组的方式来实现数独棋盘的生成。
具体的方法如下:每个九宫格做一个含有九个结点的链表,Grid[g]存储第g个九宫格链表的首结点,blocks[g]用来表示第g个九宫格中空闲位置的数量(不包括已经尝试过的结点),递归函数PutNum和GetRandomValue互相配合往合适位置填入数字,当一个九宫格中无法填入数字,则向上一个宫返回false,如果可以则继续往下一个九宫格填数字。如果每次都是用这种递归方式随机生成数独棋盘,这样子效率太低,于是我想到一个方法,每随机生成一个数独棋盘之后,可以调换数字,这样就又成了一个新的数独棋盘,考虑到左上角的数字是固定的,所以这种换数字大法可以在一个随机数独棋盘的基础上生成40320种不同的棋盘。要实现这种换数字大法,就必须获取每种全排列的顺序,我使用permutation函数来生成全排列并将获取到的数据填入arr2中。

设计实现

代码中的全局变量弄得有点多,虽然我知道这样不好,但是不这样弄得话又感觉很不方便。代码中共有6个函数:BuildLinkedList()是用来建立存储坐标的链表;GetRandomValue(short g)是用来在第g个九宫格中放置数字num,当没有合法位置放置时,返回false;PutNum(short g)是用来递归调用的函数,该函数调用GetRandomValue来放置数字num;ShowSudoku()是用来将数独棋盘输出到文本文件中的函数;Permutation(short length)是用来产生全排列数组的函数;Clean()函数是当无法生成数独棋盘的时候,对一些动态变量进行清理,防止内存泄漏。

代码说明

这个是用来在九宫格中随机选取空闲可用位置的函数

bool GetRandomValue(short g)//在第g个九宫格中随机选取可用的位置来放入数字
{if (blocks[g] == 0)return false;int value;value = rand() % blocks[g];//生成随机数int i;Node *p1, *p2;for (i = 0, p2 = Grid[g], p1 = p2; i < 2 * blocks[g] - 1; i++)//p2即为可放置数字的位置坐标{if (i >= value&&row_flag[p2->row] == false && column_flag[p2->column] == false)break;if (i == blocks[g] - 1)p2 = Grid[g], p1 = p2;else{p1 = p2;p2 = p2->next;}}if (i == 2 * blocks[g] - 1)return false;sudoku[p2->row][p2->column] = num;//接下来的代码是对被选中的目标位置结点进行删除前的准备操作numlocation[g] = 3 * (p2->row % 3) + (p2->column % 3);row_flag[p2->row] = true;column_flag[p2->column] = true;if (p2 == Grid[g])Grid[g] = p2->next;if (p2 == LinkedListTail[g])LinkedListTail[g] = p1, p1->next = NULL;if (p2 != Grid[g] && p2 != LinkedListTail[g])p1->next = p2->next;delete p2;blocks[g] = blocks[g] - 1;return true;
}

调用GetRandomValue函数在每个宫内放置数字的函数PutNum

bool PutNum(short g)//在每个九宫格中放入相应的数字num
{for (;;){if (GetRandomValue(g) == true){if (g == 8)return true;else if (PutNum(g + 1) == false)//如果PutNum(g+1)返回false,则说明第g+1个宫无法放置数字,则在第g个宫尝试可以放置的其他位置,已经尝试过的位置结点则放置到链表后面{Node *p = new Node;p->row = 3 * (g / 3) + numlocation[g] / 3;p->column = 3 * (g % 3) + numlocation[g] % 3;p->next = NULL;LinkedListTail[g]->next = p;LinkedListTail[g] = p;sudoku[p->row][p->column] = 0;row_flag[p->row] = false;column_flag[p->column] = false;continue;}else{blocks[g] = 9 - num;return true;}}else{blocks[g] = 10 - num;return false;}}
}

生成全排列并存放在arr2数组中的函数Permutation

void Permutation(short length)//用递归的方法在arr2数组中生成全排列
{int i;if (length == 10 - sudoku[0][0]){if (length != 1)Permutation(length - 1);elseShowSudoku();return;}for (i = 0; i<9 && stop_flag; i++){if (arr1[i] == 0){arr1[i] = 1;arr2[9 - length] = i + 1;if (length != 1)Permutation(length - 1);elseShowSudoku();arr1[i] = 0;}}
}

将数独棋盘输出到文本文件中

void ShowSudoku()//将数独棋盘输出到文本文件中
{int row, column;for (row = 0; row < 9; row++){for (column = 0; column < 8; column++){fcout << arr2[sudoku[row][column] - 1] << " ";//将随机生成的数独棋盘映射到arr2数组中}fcout << arr2[sudoku[row][column] - 1] << endl;}if (--sudoku_count == 0)stop_flag = false;elsefcout << endl;
}

测试运行

测试运行的截图

效能分析与改进

分析时生成的数独棋盘个数设为50000个


使用链表来进行生成数独棋盘真的很费时,效率不高,而且又占用空间。如果时间允许的话,我想不用链表来做,但是不用链表如何表示已经遍历过的位置对我来说是个问题,写完这篇博客准备去研究一下。

PSP表格

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

个人总结

第二次作业对我个人来讲难度还是很大的,这次代码用的数据结构也不是很好,处理链表带来的时间和空间开销都比较大。生成一个数独棋盘后替换数字又生成了另外一个棋盘是一个比较取巧的办法,时间开销比递归生成数独棋盘所用的时间要小,因此我采用了递归生成数独和换数字相结合的方式。还有就是这个完成这个作业的耗时远远在我的意料之外,花的时间实在是太长了,主要是前期规划不怎么好,导致编码的时候bug一大堆,很容易就停止运行了,也算是吸取一个教训了。最后一个就是自己的算法功底太薄弱了,代码也写的比较臃肿,新的学期要好好学习算法和代码的优化方法。

更新

得老师指点,将编译模式改为RELEASE模式,时间损耗是原来的四分之一,速度得到极大提升,因此对“效能分析与改进”板块进行修改,用RELEASE版的截图覆盖了原来的DEBUG版截图,并将RELEASE版的程序更新到GitHub上。
忽然发现如果将srand(time(0))放到递归函数中产生的随机数在短时间内会相等,而采用clock()函数做种子又会造成运行两次生成的2个文本文件有一定概率相等,然后尝试把srand(time(0))放到主函数中只调用一次,rand()函数放到递归函数中调用则产生的随机数即使在短时间内也不会相等,因此修改了随机数产生的代码,并将新的cpp和exe文件上传到GitHub上。

转载于:https://www.cnblogs.com/JorgeZhu/p/7498987.html

软件工程2017第二次作业相关推荐

  1. 软件工程实践2017第二次作业

    软件工程实践2017第二次作业 1)Github地址 https://github.com/Maple27/sudoku 2)解题思路 个人从小就对数独就很喜欢,对解数独有一定程度的了解,这次自己开发 ...

  2. 软件工程实践2017第二次作业-----个人项目实战之数独

    软件工程实践2017第二次作业-----个人项目实战之数独 最后一门考试2017.9.16 github地址:https://github.com/ssuo/shudu 题目地址:http://www ...

  3. 2017软件工程实践第二次作业(数独)

    我的Github项目地址,使用工具VS2017社区版 / DevC++5.11,开发语言为C语言 基础题要求如下,附加题不会做就不贴出来了...: 项目需求 利用程序随机构造出 N 个已解答的数独棋盘 ...

  4. 2017软件工程实践第二次作业

    1. 项目地址:https://github.com/one-piece-zero/sudoku 2.PSP表格记录的估计耗时 3.解题思路: 在拿到这个题目的时候,我最早想到的是大一下学期做的程序语 ...

  5. 软件工程课程第二次作业

    项目 内容 这个作业属于哪个课程 课程地址 这个作业的要求在哪里 作业要求 我在这个课程的目标是 学习软件开发,软件测试以及团队工作.大型项目开发 这个作业在哪个具体方面帮助我实现目标 第一次作业帮助 ...

  6. 软件工程实践第二次作业——个人项目实战(数独)

    作业链接 1)Github项目地址 2)在开始实现程序之前,在下述PSP表格记录下你估计将在程序的各个模块的开发上耗费的时间 见 8). 3)解题思路描述 拿到题目后,阅读了项目需求,得知这次作业要求 ...

  7. 高级软件工程课程第二次作业

    在大家了解了软件工程基本概念.流程及可行性分析后,希望各位同学结合现在流行的研究生创业创新需求,以两个人为一组,构思一个有创新的软件项目,从软件工程问题定义(可行性分析)角度,写一个研究生创新项目申报 ...

  8. 软件工程2018第二次团队作业

    传送门:软件工程(2018)团体第二次作业 一. 题目要求 请确定本团队项目的所有利益相关者,把调研结果发布到团队博客中 请团队所有成员针对目标用户确定需求调查提纲,并进行需求调研,可以采取各种你认为 ...

  9. 软件工程 第二周作业

    ##软件工程第二周作业 提出问题 1. 一般来说,想要自己的程序跑得又快又好,就要减少函数的反复调用,但有所得则必有所失,效能提高就有可能伴随着程序的稳定性的降低,这两者应该如何权衡呢? 2. 关于5 ...

最新文章

  1. vsphere---vmotion
  2. python实现单例模式的三种方法
  3. 只看不说-CCTV的客户端关键字
  4. 653. Two Sum IV - Input is a BST
  5. frp + nginx 配置多人共用的http 内网穿透服务
  6. 芯片电源引脚的电容选择
  7. 4 截图_十年漫迷舍不得删的4张截图,有大汗淋漓的香磷,满满的都是回忆
  8. 计算机维修与维护怎么学,做电脑维修需要学习哪些知识呢?
  9. C语言实现蔡勒公式,用于给定年月日计算出当前是周几
  10. 推荐5个在线免费好用的PDF转换器
  11. python画父子关系图_python elasticsearch-dsl父子关系
  12. 【Android】canScrollVertically和canScrollHorizontally
  13. 为何Adobe国际认证证书被那么多人吐槽,看完你就明白了
  14. vue项目中去空格 回车
  15. Mybatis学习总结(结合个人理解)
  16. Tarjan算法 —— 强连通双连通缩点 模板
  17. Matlab之求导篇
  18. Easy Excel 使用
  19. DevComponents.DotNetBar之SuperTabControl动态调整TABS页的位置,以动态调整按钮ButtonItem
  20. c ringbuffer 源码_【源码】RingBuffer(一)——生产者

热门文章

  1. python基础-------python2.7教程学习【廖雪峰版】(二)
  2. web office apps 在线预览实践
  3. Ubuntu录制GIF动画
  4. base64文件上传后台处理
  5. SQL Server灾难恢复方法
  6. 软件系统分析师与架构师技能大PK(您具备了哪些呢?)
  7. 浏览器是如何工作的?(转载)
  8. [翻译]延迟着色(1)
  9. NetTier模板生成的代码框架用法 (转)
  10. c语言while计算机编写,计算机等级考试二级C语言(while与dowhile循环)