数独生成算法测试
一、 正确性测试
1. 生成数独符合数独要求
保证每个生成的数独都是符合数独要求的。在debug模式下对生成的每一个数独进行检查,由于能力有限没有想到如何方便的通过GoogleTest进行测试,因此采用源代码进行测试,在代码中添加检查语句,并将数量设置为1e6,确保生成的最大1e6个数独都是符合要求的。
另外,由于算法是确定性算法,这样可以保证当输出任意数量N(1<=N<=1E6)时生成的每一个数独都是符合要求的。在数独生成算法中的showSudoku函数中添加检查算法如下:
- void showSudoku()
- {
- ++num_of_sudoku;
- //printf(" #%07d ", num_of_sudoku);
- //for (int row = 1; row < N_ROW_SUDOKU; row++)
- //{
- // cout << "-----------------------------------" << endl;
- // for (int column = 1; column < N_ROW_SUDOKU; column++)
- // {
- 10. // cout << sudoku[row][column] << " | ";
- 11. // }
- 12. // cout << " ";
- 13. //}
- 14. //cout << "-----------------------------------" << endl;
- 15. //printf(" ");
- 16.
- 17. int err = checkSudoku(sudoku);
- 18. if (err != 0)
- 19. {
- 20. cout << "wrong!" << endl;
- 21. }
- 22.
- 23. printSudoku(sudoku);
- 24.
- 25. if (num_of_sudoku == upper_limit_of_sudoku)
- 26. {
- 27. CloseHandle(h_sudoku_txt);
- 28. exit(0);
- 29. }
- }
这样就保证了生成的每一个数独都是正确的。
为提高速度,以后会把该测试注释掉。
二、 数独不重复测试
如果对1E6的每个数独都进行不重复测试显然是不可接受的。
下面分析数独生成算法。数独生成算法主要思路是生成第一行的全排列。然后根据第一行通过一系列的循环左移生成一个基本的数独盘,然后依据这个基本的数独盘进行不同行的交换即可得到更多的不同数独。
由于固定了数独左上角第一个数字为7,因此剩余8个数字的全排列为8!=40320
每一个基本盘通过行交换可以产生的数独数量是2*6*6=72种
总计该算法可生成72*40320=2903040>1e6.
在正确实施算法的情况下生成的1E6个的数独一定是不重复的。下面进行算法验证。
1. 基于同一个基本盘的数独是不重复的
这个算法很简单,只需要证明一个即可。先生成100个数独(覆盖了前72个),通过一一对照,即可证明该算法的正确性。总计只需要进行100+99+…+2+1=5050次比较。
测试思路:首先生成100(或者更多)个数独到文件中,在测试时读入这100个数独进行比较测试即可。这里的代码写在了集成测试项目里面。
2. 保证第一行的全排列是唯一的
由全排列算法可知是唯一的。由于时间关系不再测试。快速测试的思路如下:
输出数独到文件然后读入,对第一行进行哈希,这样可以降低复杂度。相同哈希值的进行比较。哈希值不同不再需要进行比较。
测试代码
#include "RepeationTest.h" #include <set> using namespace std; void closeTxtTest() { if (h_sudoku_txt != INVALID_HANDLE_VALUE) CloseHandle(h_sudoku_txt); if (h_sudoku_problem_txt != INVALID_HANDLE_VALUE) CloseHandle(h_sudoku_problem_txt); h_sudoku_problem_txt = INVALID_HANDLE_VALUE; h_sudoku_txt = INVALID_HANDLE_VALUE; } namespace RepeationTest { vector<Sudoku> buff_test; void openFileTest() { WCHAR exe_path[MAX_PATH]; GetModuleFileName(NULL, exe_path, MAX_PATH); for (int i = lstrlenW(exe_path); i >= 0; i--) { if (exe_path[i] == L'\') { exe_path[i + 1] = 0; lstrcat(exe_path, L"sudoku.txt"); break; } } h_sudoku_problem_txt = CreateFile( exe_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); SetFilePointer(h_sudoku_problem_txt, 0, 0, FILE_BEGIN); } bool readSudokuTest() { for (int i = 0; i < 1000; i++) { Sudoku tmp = nullptr; mallocSudoku(tmp); if (getSudokuFromFile(tmp) == false) { freeSudoku(tmp); return false; } buff_test.push_back(tmp); } return true; } //证明基于相同基本盘生成的数独时不重复的 bool checkRepeationBasedOneSudoku() { openFileTest(); buff_test.clear(); readSudokuTest(); for (int i = 0; i < buff_test.size(); i++) { for (int j = i + 1; j < buff_test.size(); j++) { if (checkRepeation(buff_test[i], buff_test[j])) { return true; } } freeSudoku(buff_test[i]); } return false; } } bool checkRepeationTest() { if (RepeationTest::checkRepeationBasedOneSudoku()) return true; return false; }