组合数学题如下:
:
'''
读取excel文件并转为list返回
:param filename: 文件名
:param row:
:param col:
:return:
'''
workbook = xlrd.open_workbook(filename)
sheetData = workbook.sheet_by_name('Sheet1')
# print(sheetData)
ls = []
for i in range(row, row + 21):
lt = []
for j in range(col, col + 21):
if sheetData.cell(i, j).value == 0.0:
lt.append('')
elif sheetData.cell(i, j).value == '':
lt.append('*')
else:
lt.append(int(sheetData.cell(i, j).value))
ls.append(lt)
return ls
def showList(ls):
"""
打印列表ls
:param ls:列表ls
:return:
"""
print('[')
for row in ls:
print(row)
print(']')
def repalceList(ls, row, col):
"""
从指定的ls列表取出从(row,col)取出一个9*9的子列表先完成数独并更新
:param ls: 21*21的列表
:param row: row坐标
:param col: col坐标
:return:
"""
left_top = []
for i in range(row, row + 9):
left_top.append(ls[i][col:col + 9])
# if row == 12 and col == 12:
# showList(left_top)
# 得到计算好的9*9标准数独
left_top = SudoKu.SudoKu(left_top).get_result()
# 判断生成的9*9标准数独是否有效
# print(SudoKu.judge_sudo_ku_is_legal(left_top))
# 试探出的9*9标准数独有效,则更新21*21列表的对应模块
if SudoKu.judge_sudo_ku_is_legal(left_top):
for i in range(row, row + 9):
ls[i][col:col + 9] = left_top[i - row]
def writeExcel(filename, ls):
"""
读取excel文件并转为list返回
:param ls:
:param filename: 文件名
:return:
"""
workbook = xlsxwriter.Workbook(filename) # 创建工作簿
worksheet = workbook.add_worksheet("Sheet1") # 创建子表
worksheet.activate() # 激活工作表
i = 1 # 从第1行开始写入数据
while i <= len(ls):
# 每行的第一列的位置
row = 'A' + str(i)
lt = ls[i - 1]
for j in range(len(lt)):
if lt[j] == '*':
lt[j] = ''
worksheet.write_row(row, lt)
i += 1
workbook.close() # 关闭excel文件
if __name__ == '__main__':
# 从Excel文件读取出21*21的矩阵
ls = readExcel("data/input1.xlsx", 0, 0)
print("input:")
showList(ls)
# print(showList(ls))
repalceList(ls, 0, 0)
repalceList(ls, 0, 12)
repalceList(ls, 6, 6)
repalceList(ls, 12, 0)
repalceList(ls, 12, 12)
print("output:")
showList(ls)
#结果写入Excel
# writeExcel("data/output2.xlsx", ls)
SudoKu.py
class SudoKu():
def __init__(self, sudo_ku_data):
if not isinstance(sudo_ku_data, list):
raise TypeError(f'sudo_ku_data params must a list, but {sudo_ku_data} is a {type(sudo_ku_data)}')
if len(sudo_ku_data) != 9 or len(sudo_ku_data[0]) != 9:
raise TypeError(
f'sudo_ku_data params must a 9*9 list, but {sudo_ku_data} is a {len(sudo_ku_data)}*{len(sudo_ku_data[0])} list')
self.sudo_ku = sudo_ku_data
# 存放每一行已有的数据
self.every_row_data = {}
# 每一列已有的数字
self.every_column_data = {}
# 每一个3*3有的数字
self.every_three_to_three_data = {}
# 每一个空缺的位置
self.vacant_position = []
# 每一个空缺位置尝试了的数字
self.every_vacant_position_tried_values = {}
# 初始化数据
self._init()
def _add_row_data(self, row, value):
'''
初始化的时候
添加数据到self.every_row_data中
:param row:
:param value:
:return:
'''
if row not in self.every_row_data:
self.every_row_data[row] = set()
if value in self.every_row_data[row]:
raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu')
self.every_row_data[row].add(value)
def _add_column_data(self, column, value):
'''
初始化的时候
添加数据到self.every_column_data中
:param column:
:param value:
:return:
'''
if column not in self.every_column_data:
# if not self.every_column_data.__contains__(column):
self.every_column_data[column] = set()
# if self.every_column_data.__contains__(column):
if value in self.every_column_data[column]:
raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu')
# if not self.every_column_data.__contains__(column):
self.every_column_data[column].add(value)
def _get_three_to_three_key(self, row, column):
'''
得到每一个3*3的key
:param row:
:param column:
:return:
'''
if row in [0, 1, 2]:
if column in [0, 1, 2]:
key = 1
elif column in [3, 4, 5]:
key = 2
else:
key = 3
elif row in [3, 4, 5]:
if column in [0, 1, 2]:
key = 4
elif column in [3, 4, 5]:
key = 5
else:
key = 6
else:
if column in [0, 1, 2]:
key = 7
elif column in [3, 4, 5]:
key = 8
else:
key = 9
return key
def _add_three_to_three_data(self, row, column, value):
'''
初始化的时候
添加数据到self.every_three_to_three_data中
:param row:
:param column:
:param value:
:return:
'''
key = self._get_three_to_three_key(row, column)
if key not in self.every_three_to_three_data:
self.every_three_to_three_data[key] = set()
self.every_three_to_three_data[key].add(value)
def _init(self):
'''
根据传入的数独,初始化数据
:return:
'''
for row, row_datas in enumerate(self.sudo_ku):
for column, value in enumerate(row_datas):
if value == '':
self.vacant_position.append((row, column))
else:
self._add_row_data(row, value)
self._add_column_data(column, value)
self._add_three_to_three_data(row, column, value)
def _judge_value_is_legal(self, row, column, value):
'''
判断方放置的数据是否合法
:param row:
:param column:
:param value:
:return:
'''
# value是否存在这一行数据中
if self.every_row_data.__contains__(row):
if value in self.every_row_data[row]:
return False
# value是否存在这一列数据中
if self.every_column_data.__contains__(column):
if value in self.every_column_data[column]:
return False
# value是否存在这个3*3的宫内
key = self._get_three_to_three_key(row, column)
if self.every_three_to_three_data.__contains__(key):
if value in self.every_three_to_three_data[key]:
return False
else:
return True
return True
def _calculate(self, vacant_position):
'''
计算,开始对数独进行放置值
:param vacant_position:
:return:
'''
# 得到当前位置
row, column = vacant_position
values = set(range(1, 10))
# 对当前为位置创建一个唯一key,用来存放当前位置已经尝试了的数据
key = str(row) + str(column)
# 如果这个key存在,就对values进行取差集,因为两个都是集合(set),直接使用-就行了
if key in self.every_vacant_position_tried_values:
values = values - self.every_vacant_position_tried_values[key]
# 如果这个key不存在,就创建一个空的集合
else:
self.every_vacant_position_tried_values[key] = set()
for value in values:
# 对当前数据添加到当前位置尝试过的的数据中
self.every_vacant_position_tried_values[key].add(value)
# 如果当前value合法,可以放置
if self._judge_value_is_legal(row, column, value):
# print(f'set {vacant_position} value is {value}')
# 更新 判断数据合法时 需要使用到的数据
if self.every_column_data.__contains__(column):
self.every_column_data[column].add(value)
if self.every_row_data.__contains__(row):
self.every_row_data[row].add(value)
key = self._get_three_to_three_key(row, column)
if self.every_three_to_three_data.__contains__(key):
self.every_three_to_three_data[key].add(value)
# 修改这个位置的值为value
self.sudo_ku[row][column] = value
# 返回True 和填充的 value
return True, value
return False, None
def _backtrack(self, current_vacant_position, previous_vacant_position, previous_value):
'''
回溯
:param current_vacant_position: 当前尝试失败的位置
:param previous_vacant_position: 上一次成功的位置
:param previous_value:上一次成功的值
:return:
'''
# print(f"run backtracking... value is {previous_value},vacant position is {previous_vacant_position}")
row, column = previous_vacant_position
# 对上一次成功的值从需要用到的判断的数据中移除
if self.every_column_data.__contains__(column):
self.every_column_data[column].remove(previous_value)
if self.every_row_data.__contains__(row):
self.every_row_data[row].remove(previous_value)
key = self._get_three_to_three_key(row, column)
if self.every_three_to_three_data.__contains__(key):
self.every_three_to_three_data[key].remove(previous_value)
# 并且上一次改变的的值变回去
self.sudo_ku[row][column] = ''
# 对当前尝试失败的位置已经尝试失败的的值进行删除,因为回溯了,所以下一次进来需要重新判断值
current_row, current_column = current_vacant_position
key = str(current_row) + str(current_column)
self.every_vacant_position_tried_values.pop(key)
def get_result(self):
'''
得到计算之后的数独
:return:
'''
# 空缺位置的长度
length = len(self.vacant_position)
# 空缺位置的下标
index = 0
# 存放已经尝试了的数据
tried_values = []
# 如果index小于length,说明还没有计算完
while index < length:
# 得到一个空缺位置
vacant_position = self.vacant_position[index]
# 计入计算函数,返回是否成功,如果成功,value为成功 的值,如果失败,value为None
is_success, value = self._calculate(vacant_position)
# 如果成功,将value放在tried_values列表里面,因为列表是有序的.
# index+1 对下一个位置进行尝试
if is_success:
tried_values.append(value)
index += 1
# 失败,进行回溯,并且index-1,返回上一次的空缺位置,我们需要传入当前失败的位置 和 上一次成功的位置和值
else:
if len(tried_values) > 0:
self._backtrack(vacant_position, self.vacant_position[index - 1],tried_values.pop() )
index -= 1
# 如果index<0 了 说明这个数独是无效的
if index < 0:
raise ValueError(f'{self.sudo_ku} is a invalid sudo ku')
# 打印计算之后的数独
# self.show_sudo_ku()
return self.sudo_ku
def show_sudo_ku(self):
'''
显示数独
:return:
'''
print('[')
for row in self.sudo_ku:
print(row)
print(']')
##################################################
# 用来判断最后计算的数独是否合法,和计算没有关系 #
##################################################
def judge_value_is_legal(row, column, value, sudo_ku):
# column
for i in range(0, 9):
if row == i:
continue
if value == sudo_ku[i][column]:
return False
# row
for i in range(0, 9):
if column == i:
continue
if value == sudo_ku[row][i]:
return False
# three_to_three
for i in range(row // 3 * 3, row // 3 * 3 + 3):
for j in range(column // 3 * 3, column // 3 * 3 + 3):
if i == row and j == column:
continue
if value == sudo_ku[i][j]:
return False
return True
def judge_sudo_ku_is_legal(sudo_ku):
'''
判断接出来的数独矩阵是否合法
:param sudo_ku: 数独矩阵
:return: 判断结果
'''
for row, row_values in enumerate(sudo_ku):
for column, value in enumerate(row_values):
if not judge_value_is_legal(row, column, value, sudo_ku):
return False
return True
# if __name__ == '__main__':
# # sudo_ku_data = [
# # [5, 3, '', '', 7, '', '', '', ''],
# # [6, '', '', 1, 9, 5, '', '', ''],
# # ['', 9, 8, '', '', '', '', 6, ''],
# # [8, '', '', '', 6, '', '', '', 3],
# # [4, '', '', 8, '', 3, '', '', 1],
# # [7, '', '', '', 2, '', '', '', 6],
# # ['', 6, '', '', '', '', 2, 8, ''],
# # ['', '', '', 4, 1, 9, '', '', 5],
# # ['', '', '', '', 8, '', '', 7, 9],
# # ]
# # sudo_ku_data2 = [
# # [3, '', '', '', '', 1, '', '', ''],
# # ['', '', 8, 6, '', '', '', 7, ''],
# # ['', 4, '', '', '', '', '', 2, ''],
# # [4, '', '', 2, '', '', 7, 5, 1],
# # [9, '', 1, '', 8, '', 2, '', 6],
# # ['', '', 7, 5, '', '', '', 3, 9],
# # ['', 7, 9, '', '', 3, '', 1, 2],
# # [6, 3, 2, '', 4, 5, 9, 8, 7],
# # [8, 1, 4, '', '', 9, 5, 6, 3],
# # ]
# # sudo_ku_data3 = [
# # [8, '', '', '', '', '', '', '', 4],
# # ['', 2, '', '', '', '', '', 7, ''],
# # ['', '', 9, 1, '', 6, 5, '', ''],
# # ['', '', 6, 2, '', 8, 9, '', ''],
# # ['', 9, '', '', 3, '', '', 4, ''],
# # ['', '', 2, 4, '', 7, 8, '', ''],
# # ['', '', 7, 9, '', 5, 6, '', ''],
# # ['', 8, '', '', '', '', '', 2, ''],
# # [6, '', '', '', '', '', '', '', 9],
# # ]
#
# sudo_ku_data4 = [
# [2, 5, 7, '', '', '', '', '', 9],
# [1, 9, 8, '', '', 4, '', '', 3],
# [6, 4, 3, 9, '', 2, '', '', ''],
# [8, '', '', 3, '', '', '', '', ''],
# ['', '', '', 4, '', '', '',7, ''],
# ['', '', '', '', 2, '', 1, '', 4],
# ['', '', 2, '', '', 7, '', 5, 8],
# ['', '', 6, '', '', '', '', 4, ''],
# [9, '', 5, '', '', '', 2, '', 1],
# ]
# # 得到计算好的数独
# sudo_ku = SudoKu(sudo_ku_data4).get_result()
#
# # 判断最后生成的数独是否是有效的
# print(judge_sudo_ku_is_legal(sudo_ku))
Github传送:完整代码