个人项目作业-数独

Github项目地址

时间预估

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

解题思路

  • 生成数独终局:

    • 首先生成左上角第一个3x3正方形,其中左上角格为LEFTTOP,共可生成8!=40320种组合;
    • 每一种组合都可通过(1x3或3x1的单元)排列组合扩散得到一个数独终局,记作该组合下基础数独终局;
    • 某一组合下的基础数独终局可通过分别交换456列三列、789列三列、456行三行、789行三行得到正确衍生数独终局;根据排列组合原理,任一组合下的数独终局可生成衍生数独终局(3!)^4=1296个,且不重复;
  • 求解数独:

    • 回溯法求解,当且仅当当前格赋值使得其它未填格无值可取时,更换当前格值;
    • 若数独有解,则一定能找到;

实现过程

  • 生成数独终局:

    • 左上第一个3x3方格除第一个元素(左上角)外,利用遍历全排列得到基础数块;
    • 再根据该基础数块扩散得到基础数独;
    • 分别利用456列三列、789列三列、456行三行、789行三行的全排列,替换相应行列,得到合法数独终局。
  • 求解数独:

    • 从(1,1)至(81,81)遍历,将每一次赋值压入栈中;
    • 遇某格无数可填时,弹出栈顶元素,更换取值,遍历位置回到栈顶元素,直至有值可取;
    • 因此,若数独有解,则遍历结束时,所有未完成格均合法,即找到一解。
  • 类:

    • 程序除主类外共有6个类,其中输入、输出处理模块两个类,数独结构存储三个类,计算功能一个类;
    • InputHandler类识别输入有效性,提取输入信息;
    • SudokuNode类表示每一个数独格子结点,有自身取值、所在行、列、格的指针;
    • SudokuUnit类代表行或列或格,存储各数字存在情况;
    • SudokuHead类封装9x9个SudokuNode,代表一个结构完整的数独;
    • Calculator类实现了生成、求解数独的方法;
    • OutputHandler类利用静态方法实现了数独的快速输出。
  • 单元测试设计:

    • 单元测试设计针对如下方面:

      • 非法参数
      • 数独存储结构检验
      • 基础数独的生成检验
      • 求解数独的顺序检验
    • 如下图,单元测试均通过。

优化改进

  • 生成数独:

    • 第一版生成1百万需要32.67秒,最终版需要7.15秒;
    • 优化手段:该方法生成的耗时本身不长,重点在于输出时对文件写入,以及整形转换为字符串的耗时。

    优化前:

    public static int Show(int[][] matrix)
    {
    for (int i = 0; i < 9; i++)
    {
    for(int j = 0; j < 9; j++)
    {
    sw.write((matrix[i][j]);
    sw.write(" ");
    }
    sw.write("\n");
    }
    sw.Write("\n");
    sw.Flush();
    return 0;
    }

优化后:
```
public static int Show(int[][] matrix)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
sb.Append((char)(matrix[i][j] + 48));
sb.Append(' ');
}
sb.AppendLine();
}
sw.WriteLine(sb.ToString());
return 0;
}
```

**另外一点,StreamWriter的flush()耗时较长,因此优化后设置每10000个数独flush()一次,此时flush()耗时忽略不计;其次,10000个数独对于缓冲区来说不会溢出,但缓冲区存在上限,因此应及时将其清空。**
  • 求解数独:

    • 第一版生成1百万需要5分钟...,最终版需要59秒(好不容易压进一分钟,测试数据应该属于不是那么难得数独)
    • 优化手段:削减复杂度,减少建立存储结构时的时间浪费。利用StringBuilder处理读如的一行文本信息;部分类成员变量作用域修饰符用public取代private,以空间换时间;构造SudokuHead时,减少时间复杂度;

**因改动比较细小,在此不列出代码**

关键代码

  • 生成数独关键代码部分:生成全排列

    利用离散数学中讲到的箭头生成排列方法生成,因为其每一次生成取决于上一次生成结果,因此适合用于此。
        public int generateBasic(int[] prev, bool[] arrow)
{
if(prev[0] == 0)
{
for (int i = 0; i < prev.Length; i++)
{
prev[i] = i + 1;
arrow[i] = false;
}
return 0;
}
else
{
int max = -1;
int index = -1;
for(int i = 0; i < prev.Length; i++)
{
if(arrow[i] && i == prev.Length - 1 || !arrow[i] && i == 0)
{
continue;
}
int compare = arrow[i] ? prev[i+1] : prev[i-1];
if(compare < prev[i])
{
if(prev[i] > max)
{
max = prev[i];
index = i;
}
}
}
if(max == -1)
{
return -1;
}
int tmp = prev[index];
prev[index] = arrow[index] ? prev[index + 1] : prev[index - 1];
prev[index + (arrow[index] ? 1 : -1)] = tmp;
for(int i = 0; i < prev.Length; i++)
{
if(prev[i] > tmp)
{
arrow[i] = !arrow[i];
}
}
return 0;
}
}
  • 求解数独关键代码:回溯、出入栈

    利用C#中Collections中的Stack类做栈操作
public int Solve(SudokuNode[][] nodes, Stack operationStack)
{
int count = 0;
for(int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
if(!nodes[i][j].getFlag() && nodes[i][j].getValue() != 0)
{
count++;
}
else
{
SudokuNode node = nodes[i][j];
bool[] validation = new bool[9];
if (node.getValidation(validation))
{
node.setValue(node.nextValue());
operationStack.Push(node);
}
else
{
node.reset();
SudokuNode prev = (SudokuNode)(operationStack.Pop());
i = prev.x;
j = prev.y - 1;
prev.Flag();
}
}
}
}
return 0;
}

实际耗时

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

总结

本次个人项目作业我花了非常多的时间在研究怎么优化IO上,虽然最后的成果是大幅减小了我的程序运行时间,但是和其他同学以及巨佬比起来还是太naive了,而且求解数独的算法上使用的是复杂度很高的回溯法。因此,通过这次个人项目作业,最大的收获在于认识到了自身极大的不足,继续好好学习去。。

在PSP2.1上,认为要在coding前先全部设计好,定好代码规范,然而实际上并没有认认真真好好设计。回过头看,如果在设计阶段做好了调研以及好的优化想法,那么到后来的优化会显得非常容易。因此,下次项目coding前一定做足准备工作。(更多关于这方面想say的,就放在week1另外一个作业里吧)


数独附加题Github项目地址

简单制作了一个GUI界面,支持数独题生成并显示,用户可在上进行输入,并检查答案是否正确。(不需要什么依赖包,C#wpf桌面应用)

[BUAA_SE_2017]个人项目-Sudoku的更多相关文章

  1. [BUAA_SE_2017]结对项目-数独程序扩展

    结对项目-数独程序扩展 Runnable on x64 Only sudoku17.txt 须放置在可执行文件同目录中,可移步以下链接进行下载 Core-Github项目地址 GUI-Github项目 ...

  2. Sudoku 小项目

    Sudoku 小项目 - 软工第二次作业 Part 1 · 项目相关 Github 地址: https://github.com/TheSkyFucker/Sudoku 项目的更多信息以及所有开发文档 ...

  3. 17秋 软件工程 第二次作业 sudoku

    2017年秋季 软件工程 作业2:个人项目 sudoku Github Project Github Project at Wasdns/sudoku. PSP Table PSP2.1 Person ...

  4. C++解决error C4996报错

    今天用c++写了个数独程序,在编译过程中报了一个错误: 1>------ 已启动生成: 项目: sudoku, 配置: Debug Win32 ------1> main.cpp1> ...

  5. 第二次作业——个人项目实战(Sudoku)

    Github:Sudoku 项目相关要求 利用程序随机构造出N个已解答的数独棋盘 . 输入 数独棋盘题目个数N 输出 随机生成N个 不重复 的 已解答完毕的 数独棋盘,并输出到sudoku.txt中, ...

  6. 软工实践作业2:个人项目实战之Sudoku

    Github:Sudoku 项目相关要求 项目需求 利用程序随机构造出N个已解答的数独棋盘 . 输入 数独棋盘题目个数N(0<N<=1000000). 输出 随机生成N个不重复的已解答完毕 ...

  7. Sudoku 个人项目1

    Github项目地址:Github 项目相关要求 随机构造出N个不重复的已解答的数独棋盘(0 < N <= 1000000) 在生成数独矩阵时,左上角的第一个数为:(学号后两位相加)% 9 ...

  8. 第二次作业——个人项目实战(sudoku)

    第二次作业--个人项目实战(sudoku) 一.作业要求地址 第二次作业--个人项目实战 二.Github项目地址 softengineering1--sudoku 三.PSP表格估计耗时 PSP2. ...

  9. C#开源资源项目

    一.AOP框架 Encase 是C#编写开发的为.NET平台提供的AOP框架.Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式.这种部署方面(as ...

随机推荐

  1. 离线安装PostgreSQL

    postgresql在线安装很简单,但是很多情况,服务器不能联网,需要离线安装.下面是离线安装的步骤: 1. 首先进入官网: https://www.postgresql.org/ 2. 点击Down ...

  2. 数据挖掘十大算法--K-均值聚类算法

    一.相异度计算  在正式讨论聚类前,我们要先弄清楚一个问题:怎样定量计算两个可比較元素间的相异度.用通俗的话说.相异度就是两个东西区别有多大.比如人类与章鱼的相异度明显大于人类与黑猩猩的相异度,这是能 ...

  3. 【转】Android,iOS打开手机QQ与指定用户聊天界面

    在浏览器中可以通过JS代码打开QQ并弹出聊天界面,一般作为客服QQ使用.而在移动端腾讯貌似没有公布提供类似API,但是却可以使用schema模式来启动手机QQ. 以下为具体代码: Android: S ...

  4. (零 ) 天猫精灵接入Home Assistant-总说明

    天猫精灵设备管理 https://bbs.hassbian.com/tmall 自己的hass访问地址 http://[自己的IP或域名]:8123/states 自己的MQTT服务器访问 http: ...

  5. 转载 线程池 异步I/O线程 <第三篇>

    在学习异步之前先来说说异步的好处,例如对于不需要CPU参数的输入输出操作,可以将实际的处理步骤分为以下三步: 启动处理: 实际的处理,此时不需要CPU参数: 任务完成后的处理: 以上步骤如果仅仅使用一 ...

  6. metamask源码学习-contentscript.js

    When a new site is visited, the WebExtension creates a new ContentScript in that page's context, whi ...

  7. PAT A1076 Forwards on Weibo (30 分)——图的bfs

    Weibo is known as the Chinese version of Twitter. One user on Weibo may have many followers, and may ...

  8. gitlab备份及恢复

    gitlab备份步骤 https://blog.csdn.net/qq_31666147/article/details/79844107 gitlab版本  下载地址 https://mirrors ...

  9. Python排序算法——希尔排序(Shell’s Sort)

    有趣的事,Python永远不会缺席! 如需转发,请注明出处:小婷儿的python https://www.cnblogs.com/xxtalhr/p/10793487.html 一.希尔排序(Shel ...

  10. #ifdef __cplusplus extern "C" { #endif”的定义

      平时我们在linux c平台开发的时候,引用了一些Cpp或者C的代码库,发现一些头文件有如下代码条件编译. #ifdef __cplusplus extern "C" { #e ...