您的位置:首页 > 编程语言 > Python开发

[Python]推算数独

2012-10-15 13:29 2481 查看

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

写了一段推算数独的代码,虽然很粗糙,但能解大部分的数独。(囧,有时候会解决不出,有时候还会出错,目前还没有仔细审核。现在就将就着备份在这里吧,虽然极其地不负责任~~~)

#-*- coding:utf8 -*-

import sys

data_list = [
#'''
#-69-1--3-
#--79-----
#---84---6
#---3--1--
#53--7--48
#--2--8---
#6---23---
#-----15--
#-1--8-47-
#''',
'''
3--------
---------
---------
---------
---------
---------
---------
---------
---------
''',
##very hard
#'''
#4-----1--
#7--1-5-4-
#---34-2--
#-1-42---5
#2-3-8-4-9
#8---53-1-
#--8-16---
#-7-8-4--1
#--9-----4
#''',
#
#'''
#--9----4-
#---23-6-1
#-3---6--2
#----14-76
#--6-8-4--
#45-76----
#6--8---5-
#8-7-59---
#-2----8--
#''',
#
#
#'''
#-----5-7-
#2--3-95-1
#15--2---9
#--8-63-52
#36-----18
#42-15-6--
#5---3--26
#8-12-6--5
#-9-5-----
#''',
#
#'''
#2-----57-
#---17-2-6
#---2----8
#536--2-87
#1--7-6--3
#74-3--692
#6----4---
#4-2-18---
#-15-----4
#''',
#
#
##hard
#'''
#2--4-6-7-
#--4--2---
#15-97----
#-3--6-4--
#7-1-4-9-3
#--8-9--1-
#----54-29
#---7--8--
#-2-6-9--1
#''',
#
##medium
#'''
#-67-2-3--
#--37-----
#92-1-3---
#4-2-35-6-
#3-------2
#-1-24-9-3
#---5-8-39
#-----92--
#--8-1-75-
#''',
#
###easy
#'''
#-61----9-
#43--95-6-
#9--1-2--3
#---4-1--9
#5-9---7-1
#6--2-9---
#3--9-8--6
#-8-73--42
#-9----31-
#''',
#
#'''
#--6-9---7
#84-73216-
#---1---42
#49----73-
#--3---2--
#-17----96
#93---4---
#-61329-78
#7---5-9--
#''',

]

## 将数据转换成三维数组的形式,以方便处理
def get_init_data(data):
data = data.strip()
data = data.split('\n')
result = [[] for x in data]
for i, x in enumerate(data):
for y in x:
if y=='-':result[i].append(['-'])
else: result[i].append([y])

return result

## 输出数据,方便查看和调试
def print_done_data(data):
sys.stdout.write('='*80)
sys.stdout.write('\n')
for i, x in enumerate(data):
for j, y in enumerate(x):
s = '%s'%(''.join(y))

# 如果没有填入数,则用-表示
if s=='': s='-'
sys.stdout.write('%6s'%s)

# 每三列的地方,输出分隔符
if j!=8 and j%3==2:
sys.stdout.write(' |')
else: sys.stdout.write('  ')

sys.stdout.write('\n')
# 每三行的地方,输出分隔符
if i!=8 and i%3==2:
sys.stdout.write('-'*80)
sys.stdout.write('\n')

sys.stdout.write('='*80)
sys.stdout.write('\n')

return

## 初始化阵,将各种可能的数字填入
def init_for_adjust(data):
for x in data:
for y in x:
# 对于空白的地方,填入9个数字,并用-开头做标记
if y[0]=='-':
y.extend([str(n) for n in range(1,10)])
return data

## 根据给定的行列值,得到所在的小九宫格起始位置
def get_block_ends(row, col):
rleft = 6
rright = 9
cleft = 6
cright = 9
if row<3:
rleft = 0
rright = 3
elif row<6:
rleft = 3
rright = 6

if col<3:
cleft = 0
cright = 3
elif col<6:
cleft = 3
cright = 6

return (rleft, rright, cleft, cright)

## 删除不可能的值
def remove_impossible(data):
for i,x in enumerate(data):
for j,y in enumerate(x):
# 对于确定的值,那么所有的行,列,小九宫格中将不可能再出现这个值
if len(y)==1 and y[0]!='-':

# remove_impossible row
for z in x:
if z!=y:
try:z.remove(y[0])
except:pass

#remove_impossible column
for z in data:
if z[j]!=y:
try:z[j].remove(y[0])
except:pass

#remove_impossible block
(rleft, rright, cleft, cright)=get_block_ends(i, j)
for ri in range(rleft, rright):
for ci in range(cleft, cright):
if data[ri][ci]!=y:
try : data[ri][ci].remove(y[0])
except:pass

return

## 只有一个可能值的格子,那这个格子一定是这个值
def remove_done(data):
ret = True
for x in data:
for y in x:
if len(y)==2 and y[0]=='-':
y.remove('-')
ret = False

return ret

## 确定数独的状态,这里分为错误、未完成、ok三种状态
def sukudo_state(data):
# 这里用列表来比较
DONE = [x for x in '123456789']
# test row
for row in data:
# 如果出现只有['-']的情况,那么是出错了
try:
row.index(['-'])
return 'error'
except: pass

t = []
for x in row:
t+=x
t.sort()
if t!=DONE:
# 长度相同,但内容不同,那一定是错误
if len(t)==len(DONE):
return 'error'
# 长度不同,这里认为是没有处理完
return 'not finished'

# test column
for ci in range(9):
t=[]
for row in data:
t+=row[ci]
t.sort()
if t!=DONE:
if len(t)==len(DONE):
return 'error'
return 'not finished'

# test block
for i in range(3):
rleft = i*3
rright = rleft+3
for j in range(3):
cleft = j*3
cright = cleft+3
t = []
for ti in range(rleft, rright):
for tj in range(cleft, cright):
t+=data[ti][tj]
t.sort()
if t!=DONE:
if len(t)==len(DONE):
return 'error'
return 'not finished'
return 'ok'

## 一行中只有一个格子出现某个值,那么这个格子就只能是这个值
def intelligent_row(data):
# intelligent row
for x in data:
t = []
for y in x:
if len(y)>1 and y[0]=='-':
t.extend(y[1:])
s = set(t)
for item in s:
if t.count(item)==1:
for i, y in enumerate(x):
try:
y.index(item)
x[i]=[item]
break
except:pass

## 一列中只有一个格子出现某个值,那么这个格子就只能是这个值
def intelligent_column(data):
# intelligent column
for ci in range(9):
t = []
for row in data:
if len(row[ci])>1 and row[ci][0]=='-':
t.extend(row[ci][1:])
s = set(t)
for item in s:
if t.count(item)==1:
for i, y in enumerate(data):
try:
y[ci].index(item)
data[i][ci]=[item]
break
except:pass

## 一个小九宫格中只有一个格子出现某个值,那么这个格子就只能是这个值
def intelligent_block(data):
# intelligent block
for i in range(3):
rleft = i*3
rright = rleft+3
for j in range(3):
cleft = j*3
cright = cleft+3
t = []
for ti in range(rleft, rright):
for tj in range(cleft, cright):
if len(data[ti][tj])>1 and data[ti][tj][0]=='-':
t.extend(data[ti][tj][1:])
s = set(t)
for item in s:
if t.count(item)==1:
for ti in range(rleft, rright):
for tj in range(cleft, cright):
try:
data[ti][tj].index(item)
data[ti][tj]=[item]
break
except:pass

return

## 简单地推断,直到结果不再发生变化
def easy_talent(data):
save_data = []
while cmp(save_data, data)!=0:
remove_impossible(data)
save_data = copy.deepcopy(data)

intelligent_row(data)
remove_done(data)
remove_impossible(data)
#        sys.stdout.write('\n\nafter intelligent row\n')
#        print_done_data(data)

intelligent_column(data)
remove_done(data)
remove_impossible(data)
#        sys.stdout.write('\n\nafter intelligent column\n')
#        print_done_data(data)

intelligent_block(data)
remove_done(data)
remove_impossible(data)
#        sys.stdout.write('\n\nafter intelligent block\n')
#        print_done_data(data)

remove_done(data)
remove_impossible(data)
sys.stdout.write('\n\nafter remove done data\n')
print_done_data(data)
return

#找到最少可能的格子,用于假设推算
def get_least_cell(data):
(ri, rj) = (10,10)
t = False
for i in range(9):
for j in range(9):
if data[i][j][0]=='-':
ri, rj = i, j
t = True
break
if t: break
for i in range(ri, 9):
for j in range(rj, 9):
if len(data[i][j])<len(data[ri][rj]) and data[i][j][0]=='-':
(ri,rj)=(i,j)
return (ri, rj)

## 假设推断,对格子中的各个值进行假设推断
def suppose_talent(data):
state = sukudo_state(data)
if state=='ok':
# 完成后的数独,则输出
print '* done data:'
print_done_data(data)
return
elif state=='error':
# 数独不正确,直接返回
return

# 数独没有完成时,则会来到这里进行处理
i , j = get_least_cell(data)
t = [x for x in data[i][j][1:]]
for x in t:
# 推断前,先保存当前状态
save_data = copy.deepcopy(data)
data[i][j] = [x]

# 作了假设后,继续进行简单的推断
easy_talent(data)
state =sukudo_state(data)

# 如果完成了就输出,如果还未完成,则继续假设
if state =='ok':
print '* done data:'
print_done_data(data)
return
elif state=='not finished':
suppose_talent(data)
return
# 推断的不正确时,会来到这里
# 恢复状态
data = save_data
return

import copy

if __name__=='__main__':

for data in data_list:
data = get_init_data(data)

print '*original data:'
print_done_data(data)

init_for_adjust(data)

easy_talent(data)
suppose_talent(data)

pass

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Python 数独求解