在网上找到Algorithm X in 30 lines!这篇文章,里面提供了求解数独代码,让DeepSeek把读取文件中的数独题代码加入。
#!/usr/bin/env python3
# https://www.cs.mcgill.ca/~aassaf9/python/sudoku.txt
# Author: Ali Assaf <ali.assaf.mail@gmail.com>
# Copyright: (C) 2010 Ali Assaf
# License: GNU General Public License <http://www.gnu.org/licenses/>
from itertools import product
def solve_sudoku(size, grid):
""" An efficient Sudoku solver using Algorithm X.
>>> grid = [
... [5, 3, 0, 0, 7, 0, 0, 0, 0],
... [6, 0, 0, 1, 9, 5, 0, 0, 0],
... [0, 9, 8, 0, 0, 0, 0, 6, 0],
... [8, 0, 0, 0, 6, 0, 0, 0, 3],
... [4, 0, 0, 8, 0, 3, 0, 0, 1],
... [7, 0, 0, 0, 2, 0, 0, 0, 6],
... [0, 6, 0, 0, 0, 0, 2, 8, 0],
... [0, 0, 0, 4, 1, 9, 0, 0, 5],
... [0, 0, 0, 0, 8, 0, 0, 7, 9]]
>>> for solution in solve_sudoku((3, 3), grid):
... print(*solution, sep='\\n')
[5, 3, 4, 6, 7, 8, 9, 1, 2]
[6, 7, 2, 1, 9, 5, 3, 4, 8]
[1, 9, 8, 3, 4, 2, 5, 6, 7]
[8, 5, 9, 7, 6, 1, 4, 2, 3]
[4, 2, 6, 8, 5, 3, 7, 9, 1]
[7, 1, 3, 9, 2, 4, 8, 5, 6]
[9, 6, 1, 5, 3, 7, 2, 8, 4]
[2, 8, 7, 4, 1, 9, 6, 3, 5]
[3, 4, 5, 2, 8, 6, 1, 7, 9]
"""
R, C = size
N = R * C
X = ([("rc", rc) for rc in product(range(N), range(N))] +
[("rn", rn) for rn in product(range(N), range(1, N + 1))] +
[("cn", cn) for cn in product(range(N), range(1, N + 1))] +
[("bn", bn) for bn in product(range(N), range(1, N + 1))])
Y = dict()
for r, c, n in product(range(N), range(N), range(1, N + 1)):
b = (r // R) * R + (c // C) # Box number
Y[(r, c, n)] = [
("rc", (r, c)),
("rn", (r, n)),
("cn", (c, n)),
("bn", (b, n))]
X, Y = exact_cover(X, Y)
for i, row in enumerate(grid):
for j, n in enumerate(row):
if n:
select(X, Y, (i, j, n))
for solution in solve(X, Y, []):
for (r, c, n) in solution:
grid[r][c] = n
yield grid
def exact_cover(X, Y):
X = {j: set() for j in X}
for i, row in Y.items():
for j in row:
X[j].add(i)
return X, Y
def solve(X, Y, solution):
if not X:
yield list(solution)
else:
c = min(X, key=lambda c: len(X[c]))
for r in list(X[c]):
solution.append(r)
cols = select(X, Y, r)
for s in solve(X, Y, solution):
yield s
deselect(X, Y, r, cols)
solution.pop()
def select(X, Y, r):
cols = []
for j in Y[r]:
for i in X[j]:
for k in Y[i]:
if k != j:
X[k].remove(i)
cols.append(X.pop(j))
return cols
def deselect(X, Y, r, cols):
for j in reversed(Y[r]):
X[j] = cols.pop()
for i in X[j]:
for k in Y[i]:
if k != j:
X[k].add(i)
def parse_sudoku_string(sudoku_str):
"""将字符串转换为数独网格
Args:
sudoku_str: 81个字符的字符串,0表示空格
Returns:
9x9的数独网格列表
"""
if len(sudoku_str) != 81:
raise ValueError(f"输入字符串长度必须为81,实际为{len(sudoku_str)}")
grid = []
for i in range(9):
row = []
for j in range(9):
char = sudoku_str[i*9 + j]
row.append(int(char) if char != '0' else 0)
grid.append(row)
return grid
def format_sudoku_grid(grid):
"""将数独网格格式化为字符串"""
return ''.join(str(num) for row in grid for num in row)
def solve_sudoku_file(input_file, output_file=None):
"""处理数独输入文件
Args:
input_file: 输入文件名,每行一个81位字符串
output_file: 输出文件名,如果为None则打印到控制台
"""
# 读取输入文件
with open(input_file, 'r') as f:
lines = [line.strip() for line in f if line.strip()]
results = []
# 处理每个数独问题
for idx, line in enumerate(lines):
if len(line) != 81:
print(f"警告: 第{idx+1}行长度不为81,跳过")
continue
# 复制输入行
results.append(f"输入: {line}")
try:
# 解析数独
grid = parse_sudoku_string(line)
# 求解数独
solved = False
for solution in solve_sudoku((3, 3), grid):
# 格式化解
solution_str = format_sudoku_grid(solution)
results.append(f"输出: {solution_str}")
solved = True
break # 只取第一个解
if not solved:
results.append("输出: 无解")
except Exception as e:
results.append(f"输出: 错误 - {str(e)}")
# 添加空行分隔
results.append("")
# 输出结果
if output_file:
with open(output_file, 'w') as f:
f.write('\n'.join(results))
print(f"结果已保存到 {output_file}")
else:
print('\n'.join(results))
def solve_single_sudoku(sudoku_str):
"""求解单个数独并返回结果"""
grid = parse_sudoku_string(sudoku_str)
for solution in solve_sudoku((3, 3), grid):
return format_sudoku_grid(solution)
return "无解"
def main():
"""主函数"""
import sys
# 如果没有命令行参数,使用提供的测试数据
if len(sys.argv) == 1:
test_cases = [
"000000010400000000020000000000050407008000300001090000300400200050100000000806000",
"000000010400000000020000000000050604008000300001090000300400200050100000000807000"
]
print("测试数独求解:")
print("=" * 80)
for i, test_case in enumerate(test_cases):
print(f"\n问题 {i+1}:")
print(f"输入: {test_case}")
try:
result = solve_single_sudoku(test_case)
print(f"输出: {result}")
except Exception as e:
print(f"输出: 错误 - {str(e)}")
print("-" * 80)
# 如果有命令行参数,处理文件
elif len(sys.argv) >= 2:
input_file = sys.argv[1]
output_file = sys.argv[2] if len(sys.argv) >= 3 else None
try:
solve_sudoku_file(input_file, output_file)
except FileNotFoundError:
print(f"错误: 文件 '{input_file}' 未找到")
except Exception as e:
print(f"错误: {str(e)}")
# 显示用法
else:
print("用法:")
print(" python sudoku.py # 运行测试用例")
print(" python sudoku.py input.txt # 从文件读取并输出到控制台")
print(" python sudoku.py input.txt output.txt # 从文件读取并输出到文件")
if __name__ == "__main__":
# 运行文档测试
import doctest
doctest.testmod()
# 运行主函数
main()
针对1000道17个已知数的问题,用时2秒,比上次实现的精确覆盖代码更快。可见,DLX确实是解精确覆盖问题更佳的实现。
timer64 python sudodlxpy.txt sudoku171k.txt > sudo1kans.txt
Kernel Time = 0.031 = 1%
User Time = 2.015 = 96%
Process Time = 2.046 = 98% Virtual Memory = 15 MB
Global Time = 2.085 = 100% Physical Memory = 21 MB
timer64 python ecso.txt sudoku171k.txt >ec171kans.txt
Kernel Time = 0.015 = 0%
User Time = 6.500 = 98%
Process Time = 6.515 = 98% Virtual Memory = 11 MB
Global Time = 6.612 = 100% Physical Memory = 17 MB

3386

被折叠的 条评论
为什么被折叠?



