阿煜的sudoku作业2

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/V5ZSQ/article/details/85379017
  • 性能分析

    • 生成数独

      生成数独的主要时间开销在于输出棋盘。最开始采用常规思路的printf输出,但是printf中要做正则匹配,这是没有必要的,非常的浪费时间。输出1e6个棋盘本机大概需要380s左右,后来改成了putc输出,只用了5s。

    • 解数独

      最开始的实现跑1e6组数独大概需要20分钟左右的时间,时间过长,需要改进。在读入数独后构造DLX时可以预处理每一步的状态,发现这一点之后再次进行改进,改进之后跑1e6组数独需要80s。用Visual Studio的性能测试分析工具测试了一下代码,结果是大部分的时间开销都在DLX的递归回溯过程,还有一部分时间花销在构造DLX的过程。这两个部分研究之后发现无法更进一步的改进。在学长的帮助提示之下加入多线程。包括输出输出的多线程,求解数独的多线程。最后完成了多线程的同步,解1e6组数独大概只需要30s的时间。

    • Visual Studio性能测试分析结果

在这里插入图片描述

  • 代码说明

    • 主函数部分

      主函数主要是将生成数独(create_sudoku)和求解数独(solve_sudoku)的小函数组合起来。

      int main(int argc, char* argv[])
      {
      	opterr = 0;
      	int opt = getopt(argc, argv, "c:s:");
      	if (opt == 'c')
      	{//生成数独
      		int n = to_integer(optarg);
      		if (n >= 1 && n <= 1000000)
      		{//输入格式正确
      			double start_time = clock();
      			create_sudoku(n);
      			printf("%d sudoku final status are created successfully. Execution time : %.03lf s\n", n, (clock() - start_time) / 1000);
      		}
      		else printf("ERROR! n should be an integer ranging from 1 to 1000000!\n");
      	}
      	else if (opt == 's')
      	{//求解数独
      		double start_time = clock();
      		int n = solve_sudoku(fopen(optarg, "r"));
      		printf("%d sudoku puzzles have been solved. Execution time : %.03lf s\n", n, (clock() - start_time) / 1000);
      	}
      	else
      	{//输入错误
      		printf("ERROR! Please input right command line as following:\n");
      		printf("    -c n : create n different sudoku final status.\n");
      		printf("    -s absolute_path : solve several(1~1000) sudoku puzzles.\n");
      	}
          return 0;
      }
      
      
    • 生成数独函数(create_sudoku)

      这一部分主要用到next_permutation函数

      for (int i = 0; i < 6 && n; i++)
      	{
      		if (i)
      		{
      			std::next_permutation(first + 3, first + 6);
      			first[6] = 2, first[7] = 5, first[8] = 8;
      		}
      		for (int j = 0; j < 6 && n; j++)
      		{
      			if (j) std::next_permutation(first + 6, first + 9);
      			char row[10] = "912345678";
      			for (int k = 0; k < 40320 && n; k++)
      			{
      				if (k) std::next_permutation(row + 1, row + 9);
      				for (int r = 0; r < 9; r++)
      				{
      					for (int c = 0; c < 9; c++)
      					{
      						if (c) fputc(' ', out_file);
      						fputc(row[(c + first[r]) % 9], out_file);
      					}
      					fputc('\n', out_file);
      				}
      				if (--n) fputc('\n', out_file);
      				fflush(out_file);
      			}
      		}
      	}
      
    • 求解数独函数(solve_sudoku)

      这一部分的关键是DLX的构造

      void Solver::init()
      {
      	for (int i = 0; i < 9; i++)
      		for (int j = 0; j < 9; j++)
      			for (int k = 0; k < 9; k++)
      				code[i][j][k] = enc(i, j, k);
      
      	for (int i = 0; i < 9; i++)
      		for (int j = 0; j < 9; j++)
      			for (int k = 0; k < 9; k++)
      			{
      				int r = code[i][j][k];
      				row[r][0] = code[0][i][j];
      				row[r][1] = code[1][i][k];
      				row[r][2] = code[2][j][k];
      				row[r][3] = code[3][i / 3 * 3 + j / 3][k];
      			}
      }
      
      void Solver::build(const_board_t& p)
      {
      	dlx.clear();
      	for (int i = 0; i < 9; i++)
      		for (int j = 0; j < 9; j++)
      			for (int k = 0; k < 9; k++)
      			{
      				if (p[i][j] != 0 && p[i][j] != k + 1)
      					continue;
      				int r = code[i][j][k];
      				dlx.link(r, row[r], row[r] + 4);
      			}
      }
      
  • 项目心得

    非常感谢老师出的这个小项目作业,让我学到了很多新的知识,比如DLX,比如对于一个软件项目如何跟进如何改进。这些是不去自我实践就没有办法学习到的知识。在这个数独项目的进行过程中,因为自己能力不足,求助了一些有ACM经验的集训队学长,非常感谢他们提供了很多的想法算法思路以及对于我拙劣代码的提高。从他们那里也学习到了非常多的不一样的思维角度解决问题的方式。

猜你喜欢

转载自blog.csdn.net/V5ZSQ/article/details/85379017