基于Python递归实现数独求解的代码调试求助
数独求解代码调试求助
我学习Python已有数月,尝试独立实现数独(Sudoku)求解功能,未先参考YouTube相关教程,而是自主思考编写代码,但目前陷入完全停滞的状态。查阅网上相关解决方案后,发现部分代码逻辑与我的相似,但我的代码始终无法正常运行。现寻求帮助,希望能判断是否可通过调整现有代码使其正常工作,还是我的实现思路完全偏离方向。
以下是我的代码:
# FUNCTIONS def exclude_used_numbers(): # exclude numbers already used in the same column for ii in range(1, sudoku + 1): for jj in range(sudoku): if globals()["row_" + str(ii)][jj] == 0: continue else: value_to_remove = globals()["row_" + str(ii)][jj] try: globals()["available_numbers_for_index_" + str(jj)].remove(value_to_remove) except ValueError: continue # exclude numbers already used in the same row for iii in range(1, sudoku + 1): for jjj in range(sudoku): if globals()["row_" + str(iii)][jjj] != 0: value_to_remove = globals()["row_" + str(iii)][jjj] try: globals()["available_numbers_for_row_" + str(iii)].remove(value_to_remove) except ValueError: continue # exclude numbers already used in the same square for iiii in range(1, sudoku + 1): for jjjj in range(squares_per_row): for k in range(globals()["square_" + str(jjjj + 1)][0], globals()["square_" + str(jjjj + 1)][1]): if globals()["row_" + str(iiii)][k] != 0: value_to_remove = globals()["row_" + str(iiii)][k] multiplier = ((iiii - 1) // 3) * squares_per_row try: globals()["available_numbers_for_square_" + str(jjjj + 1 + multiplier)].remove(value_to_remove) except ValueError: continue def list_of_available_nums(): # intersection of available numbers for columns and rows for a in range(1, sudoku + 1): for b in range(sudoku): if globals()["row_" + str(a)][b] == 0: temp_list = list(set(globals()["available_numbers_for_row_" + str(a)]).intersection(globals()["available_numbers_for_index_" + str(b)])) globals()["row_" + str(a)][b] = list(set(temp_list).intersection(globals()["available_numbers_for_square_" + str(b + 1)])) sudoku = 9 rows = [] # create square ranges squares = int((sudoku / 3) ** 2) squares_per_row = int(sudoku / 3) for i in range(squares_per_row): locals()["square_" + str(i+1)] = [i*3, (i*3)+3] # create row lists for i in range(1, sudoku+1): locals()["row_" + str(i)] = [] for j in range(1, sudoku+1): locals()["row_" + str(i)].append(0) rows.append(locals()["row_" + str(i)]) # create available numbers for columns for i in range(sudoku): locals()["available_numbers_for_index_" + str(i)] = [] for j in range(1, sudoku+1): locals()["available_numbers_for_index_" + str(i)].append(j) # create available numbers for rows for i in range(1, sudoku+1): locals()["available_numbers_for_row_" + str(i)] = [] for j in range(1, sudoku+1): locals()["available_numbers_for_row_" + str(i)].append(j) # create available numbers for squares for i in range(1, squares+1): locals()["available_numbers_for_square_" + str(i)] = [] for j in range(1, 10): locals()["available_numbers_for_square_" + str(i)].append(j) row_1[0] = 2 row_2[1] = 1 row_3[2] = 5 row_4[3] = 4 row_5[4] = 5 row_6[5] = 6 def solve(): exclude_used_numbers() list_of_available_nums() for i in range(1, sudoku+1): for j in range(sudoku): if isinstance(globals()["row_" + str(i)][j], int): continue elif isinstance(globals()["row_" + str(i)][j], list) and globals()["row_" + str(i)][j]: for n in globals()["row_" + str(i)][j]: globals()["row_" + str(i)][j] = n for k in range(1, sudoku + 1): for y in range(sudoku): if isinstance(globals()["row_" + str(k)][y], list): globals()["row_" + str(k)][y] = 0 print("=== GAME ===") for z in range(1, sudoku + 1): print(f"row_{z} :", end="") print(globals()["row_" + str(z)]) solve() if __name__ == '__main__': solve()
问题分析与调整建议
你的核心思路是对的:通过排除已用数字生成候选数,再用回溯法尝试填充,这是数独求解的经典路径,但代码存在几个关键问题导致无法正常运行:
1. 数据管理方式严重不合理
你用globals()/locals()动态生成变量(如row_1、available_numbers_for_index_0)来存储数独状态和候选数,这种方式不仅可读性极差,还会导致数据同步混乱——回溯修改状态后,候选数无法正确重置,后续递归会基于错误数据计算。
2. 回溯逻辑存在致命缺陷
- 填充数字后,直接将其他位置的候选数列表设为0,彻底丢失了候选数信息,递归返回后无法恢复之前的状态。
- 没有设置数独完成的终止条件,递归会无限执行。
- 尝试候选数时,未验证填充的数字是否会导致行/列/宫的冲突。
3. 宫候选数计算错误
list_of_available_nums()中用b + 1获取宫编号是错误的,单元格所在的宫应该根据行和列的位置计算,比如对于行r、列c,宫的索引应为(r//3)*3 + c//3(从0开始计数)。
4. 候选数未实时更新
exclude_used_numbers()是一次性计算候选数,但回溯过程中每次填充数字后,候选数需要重新计算,你的代码没有处理这个同步逻辑。
可落地的调整方案
无需完全重构,按以下步骤修改即可让代码正常运行:
- 改用二维列表存储数独状态:把分散的
row_1到row_9换成一个9x9的二维列表,操作更直观。 - 用三维列表存储候选数:每个单元格对应自己的候选数列表,避免分散变量的同步问题。
- 修复回溯的状态恢复:递归尝试填充数字前保存当前状态,递归返回后恢复(比如用临时变量存储,或复制候选数列表)。
- 添加终止条件:遍历数独,当所有单元格都不为0时,打印解并终止递归。
- 修正宫的索引计算:根据单元格的行和列确定所在宫的范围,正确排除已用数字。
简化修复示例
# 初始化数独棋盘 sudoku_board = [ [2,0,0,0,0,0,0,0,0], [0,1,0,0,0,0,0,0,0], [0,0,5,0,0,0,0,0,0], [0,0,0,4,0,0,0,0,0], [0,0,0,0,5,0,0,0,0], [0,0,0,0,0,6,0,0,0], [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0] ] def is_valid(r, c, num): # 检查行是否重复 if num in sudoku_board[r]: return False # 检查列是否重复 if num in [sudoku_board[i][c] for i in range(9)]: return False # 检查3x3宫是否重复 start_r = (r // 3) * 3 start_c = (c // 3) * 3 for i in range(3): for j in range(3): if sudoku_board[start_r+i][start_c+j] == num: return False return True def solve(): for r in range(9): for c in range(9): if sudoku_board[r][c] == 0: # 尝试所有可能的数字 for num in range(1, 10): if is_valid(r, c, num): sudoku_board[r][c] = num # 递归求解,成功则返回True if solve(): return True # 回溯,恢复当前单元格为0 sudoku_board[r][c] = 0 # 无可用数字,回溯 return False # 所有单元格填满,打印解 print("=== 数独解 ===") for row in sudoku_board: print(row) return True if __name__ == "__main__": solve()
内容的提问来源于stack exchange,提问作者64rl0
相关产品推荐
相关产品推荐

