同样算法的DFS求解数独C和Python程序用时比较
在网上看到一个特别简短的DFS求解数独C程序,
我把输入改成一个字符串。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
bool hang[10][10],lie[10][10],fz[10][10];//做记录
//例如 hang[1][2]=1,代表第一行有数字2
// lie[3][4]=1,代表第3列有数字4
// fz[1][5]=1,代表方针1有数字5
int sd[10][10];//代表数独的元素
// 输出
void print1() {for(int i=1;i<=9;i++) {for(int j=1;j<=9;j++) {cout<<sd[i][j]<<" ";}cout<<endl;}//system("pause");exit(0);
}
//做记录
void record(int x,int y) {hang[x][sd[x][y]]=1;lie[y][sd[x][y]]=1;fz[(x-1)/3*3+(y-1)/3+1][sd[x][y]]=1;
}
//销毁记录
void derecord(int x,int y) {hang[x][sd[x][y]]=0;lie[y][sd[x][y]]=0;fz[(x-1)/3*3+(y-1)/3+1][sd[x][y]]=0;sd[x][y]=0;
}
void dfs(int x,int y) {//当这个数原本就有if(sd[x][y]!=0) {//当搜到(9,9)时候,全部已经弄完,就输出结果if(x==9 && y==9) print1();//当搜到(x,9)时,说明这一行已经搜完了,该搜下一行了else if(y==9) dfs(x+1,1);//以上都不满足就搜这行的下一个数else dfs(x,y+1);}//当这个数原本没有else {//循环遍历,依次输入1-9,一次一次试。for(int i=1;i<=9;i++) {//如果这行,列,方阵都没有i这个数,就可以假设它是iif((!hang[x][i]) && (!lie[y][i]) && (!fz[(x-1)/3*3+(y-1)/3+1][i])) {//先假设是isd[x][y]=i;// 做记录record(x,y);//当搜到(9,9)时候,全部已经弄完,就输出结果if(x==9 && y==9) print1();//当搜到(x,9)时,说明这一行已经搜完了,该搜下一行了else if(y==9) dfs(x+1,1);//以上都不满足就搜这行的下一个数else dfs(x,y+1);//下一个格子1-9都找不到,说明假设不成立,就销毁已经做了的记录,就假设是下一个数derecord(x,y);}}//1-9都假设完了,发现还不满足,说明上一个格子假设有问题,返回上一个格子return;}
}
int main() {char s[82]="";cin>>s;for(int i=1;i<=9;i++) {for(int j=1;j<=9;j++) {sd[i][j]=s[(i-1)*9+j-1]-'0';// 不是0就做记录if(sd[i][j]) {record(i,j);}}}dfs(1,1);cout<<"该数独无解"<<endl;//system("pause");return 0;
}
让DeepSeek改写成Python,
import sys
import timeclass SudokuSolver:def __init__(self):self.r = [[False] * 10 for _ in range(10)] # 行记录self.c = [[False] * 10 for _ in range(10)] # 列记录self.g = [[False] * 10 for _ in range(10)] # 宫记录self.sd = [[0] * 10 for _ in range(10)] # 数独数组def print_sudoku(self):"""输出数独结果"""for i in range(1, 10):for j in range(1, 10):print(self.sd[i][j], end=" ")print()print()def record(self, x, y):"""做记录"""num = self.sd[x][y]self.r[x][num] = Trueself.c[y][num] = Trueg_index = (x - 1) // 3 * 3 + (y - 1) // 3 + 1self.g[g_index][num] = Truedef derecord(self, x, y):"""销毁记录"""num = self.sd[x][y]self.r[x][num] = Falseself.c[y][num] = Falseg_index = (x - 1) // 3 * 3 + (y - 1) // 3 + 1self.g[g_index][num] = Falseself.sd[x][y] = 0def dfs(self, x, y):"""深度优先搜索求解"""# 当这个数原本就有if self.sd[x][y] != 0:# 当搜到(9,9)时候,全部已经弄完,就输出结果if x == 9 and y == 9:self.print_sudoku()return True# 当搜到(x,9)时,说明这一行已经搜完了,该搜下一行了elif y == 9:return self.dfs(x + 1, 1)# 以上都不满足就搜这行的下一个数else:return self.dfs(x, y + 1)# 当这个数原本没有else:# 循环遍历,依次输入1-9,一次一次试for i in range(1, 10):g_index = (x - 1) // 3 * 3 + (y - 1) // 3 + 1# 如果这行,列,方阵都没有i这个数,就可以假设它是iif not self.r[x][i] and not self.c[y][i] and not self.g[g_index][i]:# 先假设是iself.sd[x][y] = i# 做记录self.record(x, y)result = False# 当搜到(9,9)时候,全部已经弄完,就输出结果if x == 9 and y == 9:self.print_sudoku()result = True# 当搜到(x,9)时,说明这一行已经搜完了,该搜下一行了elif y == 9:result = self.dfs(x + 1, 1)# 以上都不满足就搜这行的下一个数else:result = self.dfs(x, y + 1)# 如果找到解,直接返回if result:return True# 下一个格子1-9都找不到,说明假设不成立,就销毁已经做了的记录,就假设是下一个数self.derecord(x, y)# 1-9都假设完了,发现还不满足,说明上一个格子假设有问题,返回上一个格子return Falsedef solve(self, puzzle_str):"""解数独主函数"""# 重置所有记录self.r = [[False] * 10 for _ in range(10)]self.c = [[False] * 10 for _ in range(10)]self.g = [[False] * 10 for _ in range(10)]self.sd = [[0] * 10 for _ in range(10)]# 将字符串转换为数独数组index = 0for i in range(1, 10):for j in range(1, 10):if index < len(puzzle_str):char = puzzle_str[index]if char.isdigit() and char != '0':self.sd[i][j] = int(char)self.record(i, j)else:self.sd[i][j] = 0index += 1# 开始求解return self.dfs(1, 1)def solve_sudoku_print(puzzle_str):"""求解数独并打印结果"""solver = SudokuSolver()if not solver.solve(puzzle_str):print("该数独无解")def main():if len(sys.argv) != 2:print("用法: python script.py <文件名>")b='000000068900000002000400500041000000000035000050000000000800010300000700000100400';solve_sudoku_print(b)returnfilename = sys.argv[1]try:with open(filename, 'r', encoding='utf-8') as file:while True:line = file.readline().strip()if line == '':breakprint(f"题目: {line}")t = time.time()solve_sudoku_print(line)print(f"耗时: {round(time.time()-t, 4)} s")print("-" * 50)except FileNotFoundError:print(f"错误: 文件 '{filename}' 不存在")except Exception as e:print(f"错误: {e}")if __name__ == "__main__":main()
用selen求解器程序提供的最难级别例子测试,结果如下:
g++ backsudo.cpp -O3 -o back
time echo "000000000000003085001020000000507000004000100090000000500000073002010000000040009" | ./back
9 8 7 6 5 4 3 2 1
2 4 6 1 7 3 9 8 5
3 5 1 9 2 8 7 4 6
1 2 8 5 3 7 6 9 4
6 3 4 8 9 2 1 5 7
7 9 5 4 6 1 8 3 2
5 1 9 2 8 6 4 7 3
4 7 2 3 1 9 5 6 8
8 6 3 7 4 5 2 1 9 real 0m1.743s
user 0m1.740s
sys 0m0.000spython3 pyback.txt selensudo.txt
题目: 000000000000003085001020000000507000004000100090000000500000073002010000000040009
9 8 7 6 5 4 3 2 1
2 4 6 1 7 3 9 8 5
3 5 1 9 2 8 7 4 6
1 2 8 5 3 7 6 9 4
6 3 4 8 9 2 1 5 7
7 9 5 4 6 1 8 3 2
5 1 9 2 8 6 4 7 3
4 7 2 3 1 9 5 6 8
8 6 3 7 4 5 2 1 9 耗时: 173.0466 spypy3/bin/pypy3 pyback.txt selensudo.txt
题目: 000000000000003085001020000000507000004000100090000000500000073002010000000040009
9 8 7 6 5 4 3 2 1
2 4 6 1 7 3 9 8 5
3 5 1 9 2 8 7 4 6
1 2 8 5 3 7 6 9 4
6 3 4 8 9 2 1 5 7
7 9 5 4 6 1 8 3 2
5 1 9 2 8 6 4 7 3
4 7 2 3 1 9 5 6 8
8 6 3 7 4 5 2 1 9 耗时: 32.0108 s
--------------------------------------------------
比较结果可见,用时差100倍,改用pypy执行python程序,快了5倍。
