[Python]推算数独

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

#-*- 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

转载于:https://my.oschina.net/KavenFan/blog/83170

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值