利用DeepSeek辅助改造30行DLX求解数独python程序

编程达人挑战赛·第5期 10w+人浏览 406人参与

在网上找到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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值