Software Engineering Fundamentals - Personal Project - Sudoku


1. Project address

The code is hosted on GitHub, and the address is: https://github.com/InspAlgo/Personal_Suduku
, everyone, please remember to give me a star for the project , and I invite you to write the code!
There is also a personal project documentary that faithfully records the development process of personal projects, with many details!


2. PSP

PSP2.1 Personal Software Process Stages Estimated time (minutes) Actual time (minutes)
Planning plan
·Estimate · Estimate how long the task will take 10 20
Development develop
·Analysis · Requirements analysis (including learning new technologies) 60 120
·Design Spec · Generate design documentation 300 120
·Design Review Design review (review design documents with colleagues) / /
·Coding Standard Code Specifications (to develop appropriate specifications for current development) 120 180
·Design ·Specific design 120 180
·Coding ·Specific code 1080 1200
·Code Review ·Code review 300 60
·Test Testing (self-testing, modifying code, committing changes) 300 960
Reporting Report
·Test Report ·testing report 60 60
·Size Measurement · Computational workload 30 30
·Postmortem & Process Improvement Plan ·Summary after the fact and propose a process improvement plan 180 120
total 2560 3050

Three, problem-solving ideas

See the teacher's request document, Sudoku? Solve Sudoku problems with a computer~ What code analysis and performance testing do you need? It also needs to be hosted on github, version control with git, various specifications, and a huge number of requirements. . . There are so many questions! It's a small engineering project after all.
But in general, there are three big blocks: one is to get the Sudoku solving algorithm, which is the core; the other is code testing and performance analysis, which is different from the algorithm exercises we wrote similar to OJ before, this is a formal software programming. The necessary part; the third is to carry out version management and project specification, which is to guide the design and write code for software development from the perspective of project engineering, and it is also the significance of software engineering. In my opinion, these three points are basically considered in this personal project, and the next is the specific steps.
1. First of all, if there is a problem, find a search engine, search 数独 计算机 算法and other keywords. This step is to initially understand the appearance of the problem. First, let’s see if the predecessors have relevant experience to learn from, so that we can have a spectrum in our hearts. The key is to understand whether there are relevant algorithms.
On Zhihu, I read these very well written articles: The beauty
of Sudoku Solving Algorithms
Brutality Algorithms: How to Solve Sudoku Problems in 1 millisecond? | Brute force enumeration method + depth-first search POJ 2982
Sudoku solving algorithm Kotlin version
What are the good training methods to become a Sudoku master
Recommended:
Solving Every Sudoku Puzzle
Sudoku solving algorithm
Find on Google and Bing:
Sudoku is efficient and complete Research and Implementation of Solution Generation Algorithm
Algorithm Practice - Dancing Links Algorithm Solving Sudoku
Jumping Dancers, Dancing Links Algorithm - Solving Exact Covering Problem
[buaa-SE-2017] Personal Project
Algorithm:
2. Skip the process here. Before writing the code, I have to look at the use of github and git, because the requirements document says to see our commit situation, emmmm, so I have to build a warehouse first, but this is me No, so I have to find a tutorial again. This is also simple. Just search for an introductory tutorial. I also created a warehouse according to the tutorial. For details, you can see my personal project record .
3. I should talk about code testing and performance analysis, but considering that this is a late-stage project, in order to save time, we will complete the core algorithm first, quickly create a project prototype, and then look at this problem. Learn to do it.

Then there is the problem-solving idea of ​​the specific algorithm. After reading so many introductions to Sudoku solving methods, I feel that many of them seem to be very complicated, and there are randomization methods. However, I personally feel that the randomization method should not be desirable, because You can't tell if there are duplicates, so why don't you get a hash and check it again? That is quite memory-intensive. After all, there is a data volume of 1 million during the stress test; then the most direct method is violent backtracking, because we know that nine numbers from 1 to 9 must appear, then the final problem becomes In order to give the number row position, first give nine 1 row, then 2 row, then 3 row... and so on, and finally row to 9. Every time we arrange the position of a number, we have to perform recursion, because the recursive operation is very convenient for us to backtrack. When we arrange the numbers, we will definitely encounter the situation that we cannot arrange the numbers. At this time, we have to go back to the previous step to rearrange the numbers. just go back. The algorithm for solving Sudoku is basically the same, except that it only needs to generate an ending. We can change the return condition to whether there is a solution, because not all Sudoku problems have solutions, and there may be no solutions. , I can't determine this now, so I can't ignore the unsolved situation. For the pseudo code of the specific algorithm, please refer to my personal project record or the code description later.


Fourth, the design implementation process

4.1 Code style specification

Due to the large amount of content in this part, I have re-written a blog post. For details, please refer to Software Engineering Fundamentals Course - Personal Project - Code Style Specification .
The content in it is mainly taken from the Google C++ style guide , and I try to use this as a style standard in my personal project, which may be inconsistent in some places.

4.2 Function Diagram

The basic flow chart of the program is:

Created with Raphaël 2.1.2 开始 输入指令 分析指令 求解数独 输出 结束 生成终局 yes no

The function diagram is automatically generated by VS
这里写图片描述


Five, program performance analysis and improvement

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
From the performance analysis chart, it can be seen that my check function accounts for a large proportion. Because it is violent backtracking, it is necessary to judge whether there are repeated or non-compliant numbers at each step, and each judgment needs to be traversed by a loop, which is very expensive. It's time, I have already optimized it, and changed the horizontal and vertical loop judgment to the simultaneous loop judgment, emm, I didn't expect it to be time-consuming. Another method is to add a location record. The specific method is as follows. For example, it num_row[num][i]represents whether the number appears numin the first irow. Similarly , it num_col[num][j]represents whether the number appears numin the first jcolumn, and whether the number appears on the ninth grid. In this way, there is no need to use a loop to judge. After filling in the number, it is assigned as , and when there is no number at the beginning, it is assigned as . Then I tested it . As shown in the figure below, it can be seen that the inspection of the C strategy function has dropped to the order of 800, which was still in the order of 4000. This improvement is still effective. I even deleted the inspection function, because it is judged by this way of writing. The sentence is too short. However, it should be noted that it is restored to the appropriate position .num_box[[num][row][col]num[row][col]falsetrue

这里写图片描述
这里写图片描述

这里写图片描述
true

After basically completing the program, the performance analysis was performed again, and the results are shown below.
这里写图片描述
Among them, the function with the largest consumption is strategyC, and its specific situation is:
这里写图片描述

Compared with the previous performance analysis, there is a certain time extension, and it is found that there will be certain time fluctuations during the local test.


6. Code Description

我主要使用的是暴力回溯法生成数独的,求解数独同理。
我们看一下这个规则,会发现一个规律,就是第一行有数x后不能再有x,它一定会依次出现在后面的行,同理列也一样,然后其他数也是这样。那么我们如果按数字顺序在第一个小方格内填入1,然后再在第二行某个格填入1,依次类推直到填完9种数,到这里有人会说这样迟早会产生冲突,出现不符合规则的局面,没错,的确会这样,那么我们就从试不了的地方开始回溯,换个数继续试,直到满足位置,这里有时候会回溯好几步才能成功,不过计算机比较快,尤其是用递归实现起来还是相当快的了。
用伪代码描述:

function Backtracking_Search(i, j) //i,j表示的是大九宫格里的(i, j)格
{
    if 已生成的终局数 >= 要求生成的终局数:
        return;
    if 生成一个新终局: //检查生成新终局的便捷办法就是判断边界条件
    {            //当格子的位置移出9×9的大九宫格外说明就有新终局了
        已生成的终局数++;
        输出新生成的终局; //或者先存起来一起输出
        return;
    }

    if 到了一行的最末: //这个看你是怎么搜索了,我是按行的,也可以按列
    {
        换行到下一行开头; //我的就是i++;j=1;换列就是i=1;j++;
    }

    for num from 1 to 9:
    {
        if 已生成的终局数 >= 要求生成的终局数:
            return;
        if 准备填入(i,j)格的数num满足数独规则:
        {
            将num赋值给(i,j)格;
            Backtracking_Search(i, j);  //满足条件就继续递归下去
        }
    }

    (i, j)格重置为0; //说明填什么数字都不满足数独规则,要开始回溯,这个位置要重置为0
}

这个算法的出口就是你要生成多少终局。


七、单元测试与代码覆盖率分析

总共设计了13个测试,覆盖率为88.33%,接近90%,已达个人预期的80%。
单元测试主要针对输入的判断作了较多的测试,因为程序首先要能分析命令行指令的正确性,设计了2组正确输入也设计了3组错误输入,对参数分析作了比较完备的测试,对类的构造函数也进行了测试以判断初始化情况,对储存数组、生成数独、求解数独、输出数独等核心模块也都分别设计了测试。具体内容可以参见测试代码,不过需要注意单元测试时要将sudoku.h文件中Sudoku类private注释掉,因为我的类数据成员原本全私有,且不少方法封装的可能有点紧凑不好切入断言,故为了单元测试方便,需要改成公有。注意、注意、注意使用我的单元测试时一定要将私有改公有!!!
这里写图片描述
这里写图片描述

八、项目总结

本次个人项目历经十余天,遇到了许多奇奇怪怪的bug,感觉不再是对代码能力的考验了而是对意志力的磨练。

8.1 个人的提升

  1. 面向对象思想
    第一次正式使用VS构建C++工程,使用多文件进行对项目分模块化,而不再像之前是单文件模式,模块化构建使自己的思路与逻辑更加清晰,并且配合C++类的封装等面向对象的编程思想使得代码结构也更加清晰。这也让我感受到了面向对象编程的优秀之处,我们的世界本身就是物物的作用关系,而对象则是物物的代码抽象化,而作用关系则是方法,我们的世界中有显性的作用关系,也有隐性的,而宏观物的内部也有更加具体的微观物,这就是公有与私有、类与成员的关系。

  2. 工程能力
    什么是工程?个人认为工程是综合、是规范。综合说明了实施的过程必然是繁琐的,有一定量的,需要的知识也是多方面的,软件工程亦是如此,在本项目中,要学习C++的一些特性,还要熟悉VS的一些使用技巧,尤其是编写代码时要思考该怎么使用算法?要思考应该如何具体实现。规范则说明了代码不是随便编写的,例如首要的就是代码风格规范,有一个良好的风格规范一方面是便于阅读,另一方面则是使代码逻辑更加严谨,减少了不必要的错误。规范还体现在单元测试,之前的程序练习要求能跑就好,没有多少真正的测试,而软件开发的单元测试也对应了一般意义上的工程开发,像建楼房,有各种验收审查,各个部分的检验,而这就是代码的各个模块,单元测试是软件的一质量测试,虽实现不同但意义是一样的。

  3. 模仿与学习
    在本项目中,很多东西都是自己以前没有遇到过的,没有任何经验,只能模仿别人的方法,并通过模仿来学习自己未知的知识与经验。在这里,非常感谢一位北航学长,他的项目给我很多启发与灵感,在模仿他的一些方法的过程中学习到了很多,减少了很多弯路。模仿是学习的一种基础,先模仿获得初步的经验,有了经验我们才能更好的反思与改进,没有天生就会的,而有了足够的经验我们才能够创新与创造。
    这次的个人项目对模仿与学习能力有极大的考验,在此之前都没有过比较深入的看官方文档的经历,学会如何看文档、技术博客,从中获取自己所需要的知识与经验是一种属于成长式的能力,这种能力将会伴随我们终身并成为我们终身学习的关键一环。

8.2 不足

  1. 个人
    英文水平有待提高,在看文档的时候习惯性将网址的en-us改成zh-cn,要是没有就会启动网页翻译,博客也是中文博客,没有养成在Stack Overflow上寻找解析的习惯。对待问题总是没有彻彻底底解决的感觉,不过这个涉及的能力实在不知道该怎么说了。注意力目前还是放在了工具的使用上了,作为一名软件开发人员,我们不光要会使用轮子,更要明白轮子的构建,以及软件开发方法。

  2. 项目本身
    例如程序本身的稳定性方面没有做过较多的研究,像程序生成100万数独终局的时间就有一定的波动,没有将其压到一个较低的稳定值,也没有做多平台测试,只是在个人的Win10 x64上进行了测试。也没有去完成项目的附加题——界面程序,这个是感觉很耽误时间,这段时间要有许多科目的学习,不能将时间全砸在软工的项目上,如果是小学期课程就会好很多,没有其他课程的干扰,可以一心一意的扑在项目上。

  3. 课程设计
    这个个人项目应该有讨论群的,应该有多位助教帮忙答疑解惑。毕竟我们这是本科二年级的课程,纵然我们有一定的自学能力,但是有人指导和没人的捉瞎的感觉是不一样的。老师在课上也应该及时评阅同学的个人项目,例如中期点评啥的,这样可以避免很多不必要的错误,或者举一些实际的例子给我们演示一下,而不是照着课本讲一些目前对我们来说的很宏观的方法,工厂都有师傅带徒弟进行实操练习,这个课也应如此,讲结构设计就应该给个完整的实际例子,而不是像书上那样的言简意赅,非常抽象,讲白盒测试、黑盒测试就应该给个具体例子、具体测试工具演示一下到底是怎么弄的,只讲宏观的东西是很难弄明白的。


致谢

  1. [buaa-SE-2017]个人项目
    感谢北航辛学长的项目给了我很多灵感与经验,让我少走了许多不必要的弯路。通过学长的代码学习到了C++面向对象编程的基本方法,同时其简洁优美的代码风格与技巧让人留恋与难忘!

  2. Visual Studio 文档
    感谢微软的官方文档,让我在面对各种奇奇怪怪的错误与警告提示时有了一些基本概念与策略,同时通过文档也初步学习与掌握了单元测试方法等。其言简意赅的描述让人刻骨铭心!

  3. 【算法研究】数独高效完全解生成算法的研究和实现带你玩转Visual Studio——性能分析与优化Git 中忽略某些文件或者文件夹······
    这里无法一一列举参考过的博客,感谢大佬们的经验分享,让我在痛苦与绝望时看见了希望的光火!其开源分享的精神让人感动!

  4. 感谢老师与同学,这次的个人项目对个人确实有所历练,大概能够理解老师的用意!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325725981&siteId=291194637