利用DeepSeek辅助改造精确覆盖问题的例子求解数独

C语言的DLX求解数独程序效率虽然很高,但可读性不好,在知乎上找到精确覆盖问题这篇文章介绍得比较清楚,让DeepSeek把其中的精确覆盖问题代码和求解数独代码截图转化为文本。再把其他程序读取文件中的数独题代码加入。

from typing import Optional

class ExactCoverOfSt:
    def __init__(self, m: int) -> None:
        self.m = m  # 元素个数
        self.ests = []  # 子集列表
        self.size = [[] for _ in range(m)]  # 从元素到子集的查找索引

    def add_set(self, set: list[int]) -> None:
        i = len(self.ests)  # 当前子集的编号
        self.ests.append(set)  # set是一个整数列表,表示这个子集覆盖的元素编号。例如[0, 4, 6, 8]。
        for j in set:
            self.size[j].append(i)

    def make_exact_cover(self) -> Optional[list[int]]:
        self.covered = [False] * self.m  # 元素是否已经被覆盖
        self.selected = []  # 被选取的子集编号
        self.conflict = [False] * len(self.ests)  # 子集是否和之前已经选取的子集有冲突

        return list(self.selected) if self.dfs() else None

    def dfs(self) -> bool:
        candidate = {}
        for j in range(self.m):
            if not self.covered[j]:  # 忽略已经被覆盖的元素
                candidate[j] = [i for i in self.size[j] if not self.conflict[i]]  # 统计可选项
                if len(candidate[j]) == 0:  # 如果存在一个元素已经没有可选项了,那么一定无解,直接返回。
                    return False
        if len(candidate) == 0:  # 如果已经没有未被覆盖的元素了,说明已经找到了一个精确覆盖,直接返回。
            return True

        j = min(candidate.keys(), key=lambda x: len(candidate[x]))  # 找到可选项最少的元素
        for i in candidate[j]:
            self.selected.append(i)
            mark = []  # 记录因为选取子集i之后发生冲突的那些子集
            for k in self.ests[i]:  # 依次处理子集i覆盖的所有元素
                self.covered[k] = True
                for l in self.size[k]:  # 标记冲突
                    if not self.conflict[l]:  # 只标记由于选取子集i产生的冲突。
                        mark.append(l)
                        self.conflict[l] = True

            if self.dfs():  # 递归dfs,如果能够找到精确覆盖,则返回。
                return True

            # 如果没有找到精确覆盖,则恢复状态,尝试下一个子集。
            self.selected.pop()
            for k in self.ests[i]:
                self.covered[k] = False
            for t in mark:
                self.conflict[t] = False

        # 遍历完所有子集,仍然没有找到精确覆盖,说明无解。
        return False

def decode(subset: list[int]) -> tuple[int, int, int]:
    i = subset[0] // 9
    j = subset[0] % 9
    v = (subset[1] - 81) % 9 + 1
    return i, j, v

def solve_sudoku(grid: list[str]) -> None:
    ec = ExactCoverOfSt(81 * 4)
    for i in range(9):
        for j in range(9):
            for v in range(1, 10):
                if grid[i][j] == '.' or v == int(grid[i][j]):
                    subset = []
                    subset.append(81 * 0 + (i * 9 + j))
                    subset.append(81 * 1 + (i * 9 + v - 1))
                    subset.append(81 * 2 + (j * 9 + v - 1))
                    subset.append(81 * 3 + ((i - i % 3 + j // 3) * 9 + v - 1))
                    ec.add_set(subset)

    selected = ec.make_exact_cover()
    if selected:
        ans = [list(row) for row in grid]
        for i in selected:
            x, y, v = decode(ec.ests[i])
            ans[x][y] = str(v)
        for row in ans:
            print(''.join(row))
    else:
        print("No solution.")
'''
solve_sudoku([
    '53..7....',
    '6..195...',
    '.98....6.',
    '8...6...3',
    '4..8.3..1',
    '7...2...6',
    '.6....28.',
    '...419..5',
    '....8..79',
])

b='000000019500600000000000000600080500040000300000010000380000040000200700010900000'.replace('0','.')
solve_sudoku([b[i*9:i*9+9] for i in range(9)])
'''
import sys
import time
def main():
    if len(sys.argv) != 2:
        print("用法: python script.py <文件名>")
        return
    
    filename = sys.argv[1]
    
    try:
        with open(filename, 'r', encoding='utf-8') as file:
            while True:
                line = file.readline().strip()
                if line == '':
                    break
                print(f"题目: {line}")
                t = time.time()
                
                solve_sudoku([line.replace('0','.')[i*9:i*9+9] for i in range(9)])
                #solve(board)
                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()

针对1000道17个已知数的问题,用时6秒,基本与DLX相当。

time python ecso.txt sudoku171k.txt >ec171k.txt

有趣的是,原图中的前两个from import语句不知何故没有识别,手工加上from typing import Optional,而代码其余部分识别又相当精确,包括List Tuple中的大写,不import它们,而把代码中改为小写,在Python 3.13中是可以的。在python 3.8中报错。

line 10, in ExactCoverOfSt
    def add_set(self, set: list[int]) -> None:
TypeError: 'type' object is not subscriptable (key <class 'int'>)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值