随笔- 10  评论- 21  文章- 0 

单人项目

Github工程链接

  • 命令行程序 https://github.com/YoungForest/Sudoku-Cli
  • GUI附加题 https://github.com/YoungForest/Sudoku-GUI

PSP表

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 30
Estimate 估计这个任务需要多少时间 10 10
Analysis 需求分析 30 20
Design 具体设计 60 50
Design Spec 生成设计文档 60 60
Coding Standard 代码规范 30 20
Development 开发 300 400
Code Review 代码复审 40 45
Test 测试 200 250
Reporting 报告 60 50
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 30 30
合计 840 965

熟悉Visual Studio 2017

单元测试

助教@ChildishChange推荐的示例跳跃性太强,很多关键步骤没有截图或截图不完整,且是中文版的Visual Studio。使我这样的初学者很迷惑。

所以我选择了参考官方的测试文档。不得不说,巨硬家的文档写的就是好,
有图有真相。

单元测试还是及其好用的。上次大规模使用单元测试应该是大二下的“面向对象建模方法(OO)”的实验课了,当时的单元测试的工具是Eclipse + JAVA,并没有深度使用。单元测试的好处有很多。我现在有感受的主要有:改动代码后仍然能很方便地保证功能的正确性;测试甚至调试某一方法或函数很方便(我之前都是新建一个小工程进行测试和调试,有了单元测试这些都不需要了)。

生成数独

设计详述

我定义了一个数独格子的类,维护格子中的数和填格子的操作。首先,将数独的左上角填成5;之后,递归地从左到右填充格子;对于每一个格子,我们先生成一个包含1到9的随机列表,所填的数就是依次选取这个列表中的数。之所以这么做,是为了增加生成数独的随机性。每填一个数,判断其是否符合与行列小宫格中数不同的 要求。如果满足,判断是否数独已经填完。如果数独生成完毕,打印这个数独,并在最后一个格子上尝试下一个数;如果没填完,则继续填下一个格子。如果不满足,尝试列表中的下一个数。直到列表尝试完毕,返回上一个格子,并在上一个格子选取列表中的下一个数。

当生成了足够多的数独后,直接抛出一个异常,中断生成数独的递归程序,简单暴力。

我生成数独的算法使用的是最简单暴力的搜索法,时间复杂度很高,因为有回溯过程中有大量碰撞的情况。不过实现起来比较简单。
其实生成1000,000个数独有更取巧的方法。题目要求生成数独不重复,但并没有要求通过旋转、对称、置换、行交换、列交换等操作后仍不重复;而且通过阅读乾神的测试程序,我发现判断两个数独是否相等只是判断了两个数独的HASH值是否相等,并没有防范对称、旋转、置换后的数据不重复。

那么,一个数独通过旋转、对称、置换后可以产生多少数独呢?由于我们将左上角的数字定死了,旋转就不可以了,对称只能是从左上角到右下角的对角线对称,置换也只能是8个数字之间的置换。所以,大概会产生2! * 3! * 3! * 2! * 3! * 3! * 2 * !8 = 474656个数独。为了保证数独生成的不重复性,我们可以使用HashSet来维护。

性能分析

首次运行时的效能分析

由效能分析结果可知,花费CPU时间最多的两个内部调用是 数独生成器的FillNextGrid和PrintResult。其中,FillNextGrid是递归搜索数独结果的函数,由于搜索空间大,所需CPU时间多;PrintResult是当搜索到一个正确的数独时,打印结果到文件中去。针对这效能低下两个函数,可以由以下改进:

如何缩小搜索空间?我们可以采用生成等价数独的方法。即搜索到一个数独后,通过交换行列、数字置换、对称等方式生成与之等价却不相等的数独。由于一个数独可以生成等价数独的数目是巨大的,这样做可以有效地缩小搜索空间。当然,这种方法也有弊端:丧失了很大的随机性,产生的数独大量相关。

如何减少输出所需的时间?输出作为一种IO操作,相比CPU来说确实是很慢的。在CPU的效能分析中,输出结果函数同样占用了很多CPU时间,可能是调度等待的时间比较长。为了提升IO操作,我想到两种方法:一是多线程,计算和IO分离;二是先将结果存在一个数据结构(内存)中,累积到一定量时再一同输出。两者同时作用效果当然更好(其实是我不会用C#的多线程编程)。

求解数独

我求解数独的方法和生成数独很类似,都是使用回溯法。区别在于,生成数独时判断数字不重复只需要和左边还有上边的格子比较,求解时就还要比较了。同样存在时间复杂度较大的问题。

附加题 GUI

附加题没有算法上的难度,更多是是工程和实践上;包括错误处理,交互设计等。生成数独可以调用与CLI相同的库,增加按规定随机挖空的功能;用户填完数独提交后,还需要判断所填数独的正确性,给出相应的反馈。

我实现GUI用的是WPF(Windows Presentation Foundation),Visual Studio对其的支持非常好,开发效率也极高。我之前接触过一点UWP的开发,并没有WPF开发的经验。但是UWP开发出的程序无法生成可执行程序,不符合需求,只能生成安装包,助教测试的时候还要安装,很不方便。WPF的界面设计语言与UWP很像都是xaml的,所以之前的经验还是很有用的。

感想体会

在群里看到有些大佬使用QT完成GUI,而我用的WPF;而且大多数人使用C++完成这次作业,而我用的C#。看来我使用的工具和语言都比较小众。很多同学生成时间都比我快很多,1M的数独只需要几秒。看来我程序和算法还有很大的提高空间。

posted on 2017-09-26 21:52 YoungForest 阅读(...) 评论(...) 编辑 收藏