软件基础个人工程——数独3
GITHUB项目地址:
https://github.com/hhz-hhz/Sudoku_software_engineer.git
软工基础个人项目——数独1
软工基础个人项目——数独2
二、设计阶段
5、对于设计文档的补充
除了对于代码的设计外之前还需要对单元测试进行设计:
DefiningBeginning.cpp中:
1)int ChageStringToNumber(char s[])进行单元测试:测试输入字符串是数字的情况和输入字符串不是数字的情况
2)void WritePutsToFile(FILE* fp, SUDOKU m)进行单元测试:测试是否能够对文件进行写入
SolvingSudoku.cpp中:
1)inline bool CheckingForDFS(int n, int key)进行单元测试:需要进行符合数独规范和不符合数独规范的验证
2)inline int SolvingByDFS(int n)进行单元测试:对于一个带求解的数独进行测试
3)bool SolvingSudoku(FILE* rfp)进行单元测试:对于一个固定文件中的数独进行求解
GeneratingSudoku.cpp中:
1)inline void MovingStep()进行单元测试:查看是否能够运行出符合要求的位移矩阵
main.cpp中:
1)int main(int argc, char* argv[])进行单元测试:正确的命令和错误的命令、是否能够正确的求解数独,是否能够生成数独
三、编程阶段
1、代码说明
1)、DefiningBeginning.cpp
int ChageStringToNumber(char s[])\\利用isdigit()判断是否是数字字符
void WritePutsToFile(FILE* fp, SUDOKU m)\\由于生成数独和求解数独都需要写入文件,所以将它变成函数
2)、SolvingSudoku.cpp
检查数独:
inline bool CheckingForDFS(int n, int key)\\判断key是否能够填入 { for (int i = 0; i < 9; i++)\\判断n所在横列是否合格 { int j = n / 9; if (DoSudoku.map[j][i] == key)return false; } for (int i = 0; i < 9; i++)\\判断n所在竖列是否合格 { int j = n % 9; if (DoSudoku.map[i][j] == key)return false; } int x = n / 9 / 3 * 3; int y = n % 9 / 3 * 3; for (int i = x; i < x + 3; i++)\\判断n所在的9宫格是否合格 { for (int j = y; j < y + 3; j++) { if (DoSudoku.map[i][j] == key)return false; } } return true; }
DFS求解数独:
inline int SolvingByDFS(int n)
求解文件中的多个数独:
bool SolvingSudoku(FILE* rfp) { FILE *wfp; errno_t err; err = fopen_s(&wfp, "sudoku.txt", "w"); if (err != 0) { return false; } char blank; do { for (int i = 0; i < 9; i++)\\读入文件 { for (int j = 0; j < 9; j++) { fscanf_s(rfp, "%d%c", &DoSudoku.map[i][j], &blank, sizeof(int) + sizeof(char)); } } SolvingByDFS(0); sign = false;\\递归停止标志 WritePutsToFile(wfp, DoSudoku);\\写入文件中 } while (fscanf_s(rfp, "%c", &blank, sizeof(char)) != EOF); if (wfp != 0)fclose(wfp); return true; }
3)GeneratingSudoku.cpp
生成平移步数矩阵:MovingStepDic[72][9]
inline void MovingStep() {
\\平移方式的种类 int MovingDic1[2][3] = { { 0,3,6 },{ 0,6,3 } }; int MovingDic2[6][3] = { { 1,4,7 },{ 1,7,4 },{ 4,1,7 },{ 4,7,1 },{ 7,4,1 },{ 7,1,4 } }; int MovingDic3[6][3] = { { 2,5,8 },{ 2,8,5 },{ 5,2,8 },{ 5,8,2 },{ 8,2,5 },{ 8,5,2 } }; int step[10]; int count = 0;
\\求取2*6*6种变换方式矩阵 for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { for (int k = 0; k < 2; k++) { memcpy_s(&step[0], 3 * sizeof(int), &MovingDic1[k][0], 3 * sizeof(int)); memcpy_s(&step[3], 3 * sizeof(int), &MovingDic2[j][0], 3 * sizeof(int)); memcpy_s(&step[6], 3 * sizeof(int), &MovingDic3[i][0], 3 * sizeof(int)); memcpy_s(&MovingStepDic[count], 9 * sizeof(int), &step[0], 9 * sizeof(int)); count++; } } } }
生成数独:
bool GeneratingSudoku(int n) { FILE *fp; errno_t err; err = fopen_s(&fp, "sudoku.txt", "w"); if (err != 0) { return false; } MovingStep(); int RequestFirstline[9] = { 8,9,1,2,3,4,5,6,7 };\\按照规定,第一位是8 int JointLine[18]; int num = 0; SUDOKU ResultingSudoku; memset(ResultingSudoku.map, 0, sizeof(ResultingSudoku.map)); while (next_permutation(&RequestFirstline[1], &RequestFirstline[9]))\\生成全排列 { if (num >= n)break; for (int i = 0; i < 72; i++) { if (num >= n)break;
//生成例如891234567891234567的18位数组方便计算 memcpy(JointLine, RequestFirstline, sizeof(RequestFirstline)); memcpy(&JointLine[9], RequestFirstline, sizeof(RequestFirstline));
//对JointLine数组进行截取 int j = num % 72; for (int k = 0; k < 9; k++) { int l = MovingStepDic[j][k]; memcpy(&ResultingSudoku.map[k], &JointLine[l], 9 * sizeof(int)); }
//写入文件 WritePutsToFile(fp, ResultingSudoku); num++; } } if(fp!=0)fclose(fp); return true; }
2、代码质量分析
3、代码性能分析
1)、对于生成数独进行性能分析
可以看到fprintf和WritePutsToFile()占用较大的比重,因为WritePutsToFile()中也较多使用fprintf,所以对写入文件函数WritePutsToFile进行优化比较合适。
原写入为:
本来设计说这样的代码虽然有些粗鲁,但是比较快。因为会一次性写入9个数字及空格。如果要做优化,不如一次性写入所有元素。
优化后写入:
再对其进行性能分析:
经过优化后,时间上有了较大的改进。
2)、对于求解数独进行性能分析