数独-性能改进
代码质量分析
? 用SonarLint插件对每个java文件进行代码质量优化
? 分析过后,除了Main
类中因为需要打印命令错误提示,仍存在一些warning,其余文件中的warning全部消除。
性能改进
??用Intellij中插件JProfiler进行程序性能分析
??最初的程序每生成一个终局,直接写入文件,且进行回溯时,多次进行值的交换。为了保证随机性,每一次将所有可能值先放入链表,然后将随机选取一个放入该空位。在求解数独时,先读取一个数独,求解后写入文件,再继续读取下一个数据。这种算法会频繁创建很多对象,且读写会花费大量的时间。
??改进后,时间上有了非常大的提升,且在回溯,用一个27位数组Criterion
,前九位表示每一行可能解,中间九位表示每一列可能解,最后九位表示每一个宫的可能解,且每一个数可以表示成一个九位的二进制,从右到左依次表示1-9,若Criterion[0]=000_000_001,则表示第一行只有1没有用过,该位置的可行解只有1。选择一个数时只需将该数与对应的下标的Criterion
进行与运算,即可将该数标记为使用,这样减少了反复复制的时间。且改进后不再随机选取可行解,按顺序进行选择。
??并且改进后每生成一个终局,先放在一个char数组中,生成所有的终局,统一写入文件;在求解数独时,一次读取出所有的待求解数独,每个分别求解,再统一写入。
??下图为Jprofiler中提供的CallTree,通过树形图体现了方法间的层次调用关系,并按照执行总时间从小到大排列。
??改进之前,用FileWriter写入文件,生成1000个终局需要12s左右。每生成一个终局马上写入文件。下图看到关闭流花费了大量的时间。
??改进后,用BufferedWrite写入文件,由于含有缓冲区,所以效率要比FileWriter高,但内部仍用FileWrite进行读写。在生成1000个终局时,总耗时为0.034s。由下表写入文件所消耗的时间远大于生成终局。
??以后讨论情况均为改进以后
??在生成1,000,000个终局时,总耗时0.805。stringToFile,即将终局写入文件所占比例最大,即耗时最长,但生成终局的方法generateSudoku执行耗时虽然不是最长,但仍占有较大的比例。
??综合生成1000和1,000,000个终局可得,在生成终局的情况下,stringToFile时消耗最多的函数。
??在求解数独时,首先读取1000个数独,进行求解,总耗时0.325s。此时findSolution函数耗时最多,但在这个函数中会调用其他函数,其中dealFile即读取文件耗时最多。
??求解10,000个数独时,findSolution耗时最多,在该函数调用的函数中,SolveSudoku求解数独的函数耗时超过了读取文件的耗时。在求解数目较小,读取文件的时间相对较长,在求解数独变大以后,求解数独的时间要更长。