DeepSeek辅助编写的计算数独可选数的python程序第2部分
·
上文只计算了初始的唯一可选数
在上述程序基础上,将具有1个可选数的格子一次性都填上唯一可选数,用新字符串重新计算可选数,直到没有唯一可选数的格子,输出每轮迭代的结果,给出完整的python程序
def find_candidates(board):
"""找出数独中每个空格的所有可能数字"""
# 初始化候选数集合,每个空格初始包含1-9
candidates = [[set(range(1, 10)) for _ in range(9)] for _ in range(9)]
# 根据已有数字减少候选数
for r in range(9):
for c in range(9):
pos = r * 9 + c
if board[pos] != '0':
num = int(board[pos])
# 清除同行、同列、同宫的候选数
candidates[r][c] = {num} # 已确定数字的位置
# 清除同行
for cc in range(9):
if cc != c and num in candidates[r][cc]:
candidates[r][cc].remove(num)
# 清除同列
for rr in range(9):
if rr != r and num in candidates[rr][c]:
candidates[rr][c].remove(num)
# 清除同宫(3x3区块)
start_r, start_c = 3 * (r // 3), 3 * (c // 3)
for rr in range(start_r, start_r + 3):
for cc in range(start_c, start_c + 3):
if (rr != r or cc != c) and num in candidates[rr][cc]:
candidates[rr][cc].remove(num)
return candidates
def format_cell(candidates, board, r, c):
"""格式化单个格子,确保输出为9个字符(不含括号)"""
pos = r * 9 + c
cell_chars = ""
if board[pos] != '0':
# 预置数字,用{}包裹
cell_chars = f"{{{board[pos]}}}"
else:
cand_set = sorted(candidates[r][c])
if not cand_set:
# 无候选数的情况(理论上有候选数)
cell_chars = "[]"
elif len(cand_set) == 1:
# 只有一个候选数,用<>包裹
cell_chars = f"<{cand_set[0]}>"
else:
# 多个候选数,用[]包裹
cand_str = "".join(str(n) for n in cand_set)
cell_chars = f"[{cand_str}]"
# 确保输出为9个字符
# 计算需要填充的字符数
content_len = len(cell_chars) # 括号内的内容长度
brackets_len = 2 # 两个括号
total_len = content_len + brackets_len
if total_len < 9:
# 在括号内填充空格使总长度为9
padding = 9 - total_len
left_pad = padding // 2
right_pad = padding - left_pad
cell_chars = cell_chars[0] + " " * left_pad + cell_chars[1:-1] + " " * right_pad + cell_chars[-1]
return cell_chars
def print_sudoku_candidates(board_str, candidates, iteration, changed_cells=None):
"""打印数独候选数矩阵"""
print(f"\n{'=' * 90}")
print(f"迭代轮次 {iteration}:")
print(f"{'=' * 90}")
# 打印每行
for r in range(9):
row_chars = []
for c in range(9):
cell = format_cell(candidates, board_str, r, c)
row_chars.append(cell)
# 添加宫格分隔符
formatted_row = ""
for i, cell in enumerate(row_chars):
formatted_row += cell
if i in [2, 5]: # 第3、6列后加分隔符
formatted_row += " | "
elif i < 8:
formatted_row += " "
print(formatted_row)
# 添加宫格行分隔符
if r in [2, 5]:
print("-" * 90)
print("=" * 90)
# 打印统计信息
total_empty = board_str.count('0')
single_candidates = 0
multi_candidates = 0
for r in range(9):
for c in range(9):
pos = r * 9 + c
if board_str[pos] == '0':
if len(candidates[r][c]) == 1:
single_candidates += 1
else:
multi_candidates += 1
print(f"空格总数: {total_empty}")
print(f"只有一个候选数的位置: {single_candidates}")
print(f"有多个候选数的位置: {multi_candidates}")
if changed_cells:
print(f"本轮填充的格子: {changed_cells}")
def fill_unique_candidates(board_str, candidates):
"""填充所有只有一个候选数的格子,返回新的数独字符串"""
board_list = list(board_str)
changed_cells = []
for r in range(9):
for c in range(9):
pos = r * 9 + c
if board_list[pos] == '0' and len(candidates[r][c]) == 1:
# 只有一个候选数,填充它
num = next(iter(candidates[r][c]))
board_list[pos] = str(num)
changed_cells.append((r, c, num))
new_board = "".join(board_list)
return new_board, changed_cells
def solve_iteratively(initial_board):
"""迭代求解数独,每次填充唯一候选数"""
print("开始迭代求解数独...")
print(f"初始数独: {initial_board}")
current_board = initial_board
iteration = 0
all_iterations = []
while True:
iteration += 1
# 计算候选数
candidates = find_candidates(current_board)
# 统计当前状态
total_empty = current_board.count('0')
single_candidates_count = 0
for r in range(9):
for c in range(9):
pos = r * 9 + c
if current_board[pos] == '0' and len(candidates[r][c]) == 1:
single_candidates_count += 1
print(f"\n{'*' * 90}")
print(f"第 {iteration} 轮迭代 - 空格数: {total_empty}, 唯一候选数: {single_candidates_count}")
print(f"{'*' * 90}")
# 打印当前候选数矩阵
print_sudoku_candidates(current_board, candidates, iteration)
# 保存当前状态
all_iterations.append({
'board': current_board,
'candidates': [[sorted(candidates[r][c]) for c in range(9)] for r in range(9)],
'iteration': iteration
})
# 检查是否有唯一候选数可以填充
if single_candidates_count == 0:
print(f"\n迭代结束:没有更多唯一候选数可填充")
break
# 填充唯一候选数
new_board, changed_cells = fill_unique_candidates(current_board, candidates)
# 检查是否有变化
if new_board == current_board:
print(f"\n迭代结束:没有发现需要填充的格子")
break
# 显示填充的信息
if changed_cells:
print(f"\n本轮填充了 {len(changed_cells)} 个格子:")
for r, c, num in changed_cells:
print(f" 位置 ({r+1}, {c+1}) 填充数字 {num}")
# 更新当前数独
current_board = new_board
# 检查是否已经解决
if current_board.count('0') == 0:
print(f"\n🎉 数独已完全解决!")
break
# 安全检查:最多迭代20次
if iteration >= 20:
print(f"\n⚠️ 已达到最大迭代次数(20次),停止迭代")
break
return current_board, all_iterations
def print_summary(initial_board, final_board, all_iterations):
"""打印求解摘要"""
print(f"\n{'=' * 90}")
print("求解摘要")
print(f"{'=' * 90}")
initial_empty = initial_board.count('0')
final_empty = final_board.count('0')
filled_cells = initial_empty - final_empty
print(f"初始空格数: {initial_empty}")
print(f"最终空格数: {final_empty}")
print(f"填充的格子数: {filled_cells}")
print(f"迭代轮次: {len(all_iterations)}")
# 显示最终结果
if final_empty == 0:
print(f"\n🎉 数独已完全解决!最终结果:")
else:
print(f"\n⚠️ 数独未完全解决,剩余 {final_empty} 个空格。最终状态:")
# 显示最终数独的网格形式
print("\n最终数独网格:")
print("+" + "-" * 23 + "+")
for r in range(9):
row = ""
for c in range(9):
pos = r * 9 + c
num = final_board[pos]
if num == '0':
row += ". "
else:
row += num + " "
if c in [2, 5]:
row += "| "
print("| " + row + "|")
if r in [2, 5]:
print("|" + "-" * 23 + "|")
print("+" + "-" * 23 + "+")
def main():
"""主函数"""
print("数独唯一候选数迭代求解器")
print("=" * 50)
# 示例数独
examples = {
"1": "530070000600195000098000060800060003400803001700020006060000280000419005000080079", # 中等难度
"2": "000000010400000000020000000000050407008000300001090000300400200050100000000806000", # 困难
"3": "000000000000003085001020000000507000004000100090000000500000073002010000000040009", # 极难
"4": "800000000003600000070090200050007000000045700000100030001000068008500010090000400", # 世界最难数独之一
}
print("可用示例:")
for key, board in examples.items():
empty = board.count('0')
print(f" 示例{key}: {board} (空格数: {empty})")
while True:
print("\n" + "=" * 50)
choice = input("请选择:\n1. 使用示例\n2. 手动输入数独\n3. 退出\n选择 (1/2/3): ").strip()
if choice == '3':
print("程序退出")
break
if choice == '1':
ex_choice = input("选择示例 (1/2/3/4): ").strip()
if ex_choice in examples:
board_str = examples[ex_choice]
print(f"使用示例{ex_choice}: {board_str}")
else:
print("无效选择,使用示例1")
board_str = examples['1']
elif choice == '2':
print("\n请输入81个字符的数独字符串(0表示空格):")
while True:
board_str = input("数独: ").strip()
if len(board_str) != 81:
print(f"错误:输入长度必须为81字符,当前长度为{len(board_str)}")
continue
if not set(board_str).issubset(set('0123456789')):
print("错误:输入只能包含数字0-9")
continue
if board_str.count('0') == 81:
print("错误:数独不能全为空")
continue
break
else:
print("无效选择")
continue
# 开始迭代求解
final_board, all_iterations = solve_iteratively(board_str)
# 打印摘要
print_summary(board_str, final_board, all_iterations)
# 询问是否继续
cont = input("\n是否继续求解另一个数独? (y/n): ").strip().lower()
if cont != 'y':
print("程序退出")
break
if __name__ == "__main__":
main()
输出如下
C:\d\1230>python candnum2.py
数独唯一候选数迭代求解器
==================================================
可用示例:
示例1: 530070000600195000098000060800060003400803001700020006060000280000419005000080079 (空格数: 51)
示例2: 000000010400000000020000000000050407008000300001090000300400200050100000000806000 (空格数: 64)
示例3: 000000000000003085001020000000507000004000100090000000500000073002010000000040009 (空格数: 64)
示例4: 800000000003600000070090200050007000000045700000100030001000068008500010090000400 (空格数: 60)
请选择:
1. 使用示例
2. 手动输入数独
3. 退出
选择 (1/2/3): 1
选择示例 (1/2/3/4): 1
使用示例1: 530070000600195000098000060800060003400803001700020006060000280000419005000080079
开始迭代求解数独...
初始数独: 530070000600195000098000060800060003400803001700020006060000280000419005000080079
******************************************************************************************
第 1 轮迭代 - 空格数: 51, 唯一候选数: 4
******************************************************************************************
==========================================================================================
迭代轮次 1:
==========================================================================================
{ 5 } { 3 } [ 124 ] | [ 26 ] { 7 } [2468 ] | [1489 ] [1249 ] [ 248 ]
{ 6 } [ 247 ] [ 247 ] | { 1 } { 9 } { 5 } | [3478 ] [ 234 ] [2478 ]
[ 12 ] { 9 } { 8 } | [ 23 ] [ 34 ] [ 24 ] | [13457] { 6 } [ 247 ]
------------------------------------------------------------------------------------------
{ 8 } [ 125 ] [1259 ] | [ 579 ] { 6 } [ 147 ] | [4579 ] [2459 ] { 3 }
{ 4 } [ 25 ] [2569 ] | { 8 } < 5 > { 3 } | [ 579 ] [ 259 ] { 1 }
{ 7 } [ 15 ] [1359 ] | [ 59 ] { 2 } [ 14 ] | [4589 ] [ 459 ] { 6 }
------------------------------------------------------------------------------------------
[ 139 ] { 6 } [134579] | [ 357 ] [ 35 ] < 7 > | { 2 } { 8 } < 4 >
[ 23 ] [ 278 ] [ 237 ] | { 4 } { 1 } { 9 } | [ 36 ] < 3 > { 5 }
[ 123 ] [1245 ] [12345] | [2356 ] { 8 } [ 26 ] | [1346 ] { 7 } { 9 }
==========================================================================================
空格总数: 51
只有一个候选数的位置: 4
有多个候选数的位置: 47
本轮填充了 4 个格子:
位置 (5, 5) 填充数字 5
位置 (7, 6) 填充数字 7
位置 (7, 9) 填充数字 4
位置 (8, 8) 填充数字 3
******************************************************************************************
第 2 轮迭代 - 空格数: 47, 唯一候选数: 5
******************************************************************************************
==========================================================================================
迭代轮次 2:
==========================================================================================
{ 5 } { 3 } [ 124 ] | [ 26 ] { 7 } [2468 ] | [1489 ] [1249 ] [ 28 ]
{ 6 } [ 247 ] [ 247 ] | { 1 } { 9 } { 5 } | [3478 ] [ 24 ] [ 278 ]
[ 12 ] { 9 } { 8 } | [ 23 ] [ 34 ] [ 24 ] | [13457] { 6 } [ 27 ]
------------------------------------------------------------------------------------------
{ 8 } [ 125 ] [1259 ] | [ 79 ] { 6 } [ 14 ] | [4579 ] [2459 ] { 3 }
{ 4 } < 2 > [ 269 ] | { 8 } { 5 } { 3 } | [ 79 ] [ 29 ] { 1 }
{ 7 } [ 15 ] [1359 ] | < 9 > { 2 } [ 14 ] | [4589 ] [ 459 ] { 6 }
------------------------------------------------------------------------------------------
[ 139 ] { 6 } [1359 ] | [ 35 ] < 3 > { 7 } | { 2 } { 8 } { 4 }
< 2 > [ 278 ] [ 27 ] | { 4 } { 1 } { 9 } | < 6 > { 3 } { 5 }
[ 123 ] [1245 ] [12345] | [2356 ] { 8 } [ 26 ] | [ 16 ] { 7 } { 9 }
==========================================================================================
空格总数: 47
只有一个候选数的位置: 5
有多个候选数的位置: 42
本轮填充了 5 个格子:
位置 (5, 2) 填充数字 2
位置 (6, 4) 填充数字 9
位置 (7, 5) 填充数字 3
位置 (8, 1) 填充数字 2
位置 (8, 7) 填充数字 6
*
*
*
******************************************************************************************
第 10 轮迭代 - 空格数: 2, 唯一候选数: 2
******************************************************************************************
==========================================================================================
迭代轮次 10:
==========================================================================================
{ 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 }
==========================================================================================
空格总数: 2
只有一个候选数的位置: 2
有多个候选数的位置: 0
本轮填充了 2 个格子:
位置 (4, 3) 填充数字 9
位置 (9, 2) 填充数字 4
🎉 数独已完全解决!
==========================================================================================
求解摘要
==========================================================================================
初始空格数: 51
最终空格数: 0
填充的格子数: 51
迭代轮次: 10
🎉 数独已完全解决!最终结果:
最终数独网格:
+-----------------------+
| 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 |
+-----------------------+
是否继续求解另一个数独? (y/n): y
==================================================
请选择:
1. 使用示例
2. 手动输入数独
3. 退出
选择 (1/2/3): 1
选择示例 (1/2/3/4): 2
使用示例2: 000000010400000000020000000000050407008000300001090000300400200050100000000806000
开始迭代求解数独...
初始数独: 000000010400000000020000000000050407008000300001090000300400200050100000000806000
******************************************************************************************
第 1 轮迭代 - 空格数: 64, 唯一候选数: 1
******************************************************************************************
==========================================================================================
迭代轮次 1:
==========================================================================================
[56789] [36789] [35679] | [235679] [234678] [2345789] | [56789] { 1 } [2345689]
{ 4 } [136789] [35679] | [235679] [123678] [1235789] | [56789] [2356789] [235689]
[156789] { 2 } [35679] | [35679] [134678] [1345789] | [56789] [3456789] [345689]
------------------------------------------------------------------------------------------
[ 269 ] [ 369 ] [2369 ] | [ 236 ] { 5 } [1238 ] | { 4 } [2689 ] { 7 }
[25679] [4679 ] { 8 } | [ 267 ] [12467] [1247 ] | { 3 } [2569 ] [12569]
[2567 ] [3467 ] { 1 } | [2367 ] { 9 } [23478] | [ 568 ] [2568 ] [2568 ]
------------------------------------------------------------------------------------------
{ 3 } [16789] [ 679 ] | { 4 } < 7 > [ 579 ] | { 2 } [56789] [15689]
[26789] { 5 } [24679] | { 1 } [ 237 ] [2379 ] | [6789 ] [346789] [34689]
[1279 ] [1479 ] [2479 ] | { 8 } [ 237 ] { 6 } | [1579 ] [34579] [13459]
==========================================================================================
空格总数: 64
只有一个候选数的位置: 1
有多个候选数的位置: 63
本轮填充了 1 个格子:
位置 (7, 5) 填充数字 7
******************************************************************************************
第 2 轮迭代 - 空格数: 63, 唯一候选数: 0
******************************************************************************************
==========================================================================================
迭代轮次 2:
==========================================================================================
[56789] [36789] [35679] | [235679] [23468] [2345789] | [56789] { 1 } [2345689]
{ 4 } [136789] [35679] | [235679] [12368] [1235789] | [56789] [2356789] [235689]
[156789] { 2 } [35679] | [35679] [13468] [1345789] | [56789] [3456789] [345689]
------------------------------------------------------------------------------------------
[ 269 ] [ 369 ] [2369 ] | [ 236 ] { 5 } [1238 ] | { 4 } [2689 ] { 7 }
[25679] [4679 ] { 8 } | [ 267 ] [1246 ] [1247 ] | { 3 } [2569 ] [12569]
[2567 ] [3467 ] { 1 } | [2367 ] { 9 } [23478] | [ 568 ] [2568 ] [2568 ]
------------------------------------------------------------------------------------------
{ 3 } [1689 ] [ 69 ] | { 4 } { 7 } [ 59 ] | { 2 } [5689 ] [15689]
[26789] { 5 } [24679] | { 1 } [ 23 ] [ 239 ] | [6789 ] [346789] [34689]
[1279 ] [1479 ] [2479 ] | { 8 } [ 23 ] { 6 } | [1579 ] [34579] [13459]
==========================================================================================
空格总数: 63
只有一个候选数的位置: 0
有多个候选数的位置: 63
迭代结束:没有更多唯一候选数可填充
==========================================================================================
求解摘要
==========================================================================================
初始空格数: 64
最终空格数: 63
填充的格子数: 1
迭代轮次: 2
⚠️ 数独未完全解决,剩余 63 个空格。最终状态:
最终数独网格:
+-----------------------+
| . . . | . . . | . 1 . |
| 4 . . | . . . | . . . |
| . 2 . | . . . | . . . |
|-----------------------|
| . . . | . 5 . | 4 . 7 |
| . . 8 | . . . | 3 . . |
| . . 1 | . 9 . | . . . |
|-----------------------|
| 3 . . | 4 7 . | 2 . . |
| . 5 . | 1 . . | . . . |
| . . . | 8 . 6 | . . . |
+-----------------------+
是否继续求解另一个数独? (y/n):
更多推荐
所有评论(0)