python数独的完整解法_数独人工解法的一些技巧及其python实现

def _nakedSingleNumber( self ):

self._changed = False

for pos, validset in self._valid_set.items():

if len(validset)<=0:

self._invalid = False

elif len(validset) == 1:

num = validset.pop()

validset.add(num)

print 'pos', pos, 'has only one candidate: %d' %num

self._changed = True

return True

return False

def _hiddenSingleNumber( self ):

self._changed = False

for num, posset in self._candi_pos.items():

#_groupByRow,_groupByCol,_groupByBlock is helper function

rows = self._groupByRow(posset)

for r, row in rows.items():

if len(row)==1:

print 'row%d has only one position for num %d' %(r, num), row[0]

self._changed = True

return True

cols = self._groupByCol(posset)

for c, col in cols.items():

if len(col)==1:

print 'col%d has only one position for num %d' %(c, num), col[0]

self._changed = True

return True

blks = self._groupByBlock(posset)

for b, blk in blks.items():

if len(blk)==1:

print 'blk%d has only one position for num %d' %(b, num), blk[0]

self._changed = True

return True

return False

从我们对显式数对/三数集/四数集法的描述,可以看出,它们其实很相似,只不过涉及到的候选数的个数不相同罢了。显式数对法中,要求两个位置有且只有两个相同的候选数,显式三数集法中,要求有三个位置,他们可以有两个或三个候选数,但是他们涉及到的候选数合起来正好是三个;同理,显式四数集法中要求有四个位置,他们可以有两个、三个或四个候选数,但是他们涉及到的候选数合起来正好是四个。注意到的是这些个位置指的都是同一个房内的。因此我的思路是这样的:分别对行/列/宫进行分析,num表示数集的个数(num=2:数对,num=3:三数集,num=4:四数集),如果该位置的候选数个数小于num,说明这个位置是可选的,将它加入candidate list中。从candidate list中任选num个(当然前提是candidate list中至少有num个元素),求这些位置的候选数的并集,如果正好等于num,则说明他们形成了显式数集。这其中涉及到一个辅助函数,_get_n_from_m(n,m),即从m中任选n个元素而不导致重复,也就是我们的Cm取n,实现如下:

def _get_n_from_m( self, n, m ):

index = range(n)

while index[0]<=m-n:

yield index

i = n-1

if index[i]

index[i]+=1

else:

index[i-1]+=1

while i>0 and index[i-1]>=m-n+i:

i -= 1

index[i-1]+=1

for j in range(i,n):

index[j]=index[j-1]+1

这个函数很有用,在后面将多次使用到。有了这个函数,接下来实现我们的思路就不难了:

#nakedNumberSet occurs in a [house], [name] it 'row/column/block'

#num=2: pair

#num=3: tripple set

#num=4: quad set

def _helper_nakedNumberSet( self, house, name, num ):

for i, item in house.items():

candidate = []

#if a position has less than or equal to [num] candidates

#add it to the candidate position list

for pos in item:

if len(self._valid_set[pos])<=num:

candidate.append(pos)

#if candidate list has at least [num] elements

#get any [num] of positions

#and have union of candidate numbers of these position

#if this union has exactly [num] candidate numbers

#we can delete these candidate numbers from cells other than these position

if len(candidate)>=num:

for indices in self._get_n_from_m( num, len(candidate)):

uSet = set()

subset = []

toRemove = []

for index in indices:

uSet = uSet.union(self._valid_set[candidate[index]])

if len(uSet)==num:

subset = [ candidate[j] for j in indices ]

for j in subset:

item.remove(j)

for pos in item:

for n in uSet:

if n in self._valid_set[pos]:

toRemove.append( ( pos[0], pos[1], n ))

if toRemove:

print 'in %s%d,' %(name,i),

for (r,c) in subset:

print '(%d, %d)' %(r,c),

print 'have only candidates',

for n in uSet:

print '%d' %n,

print

self._removeCandidate(toRemove)

self._changed = True

return True

return False

同样的,隐式数对/三数集/四数集法也是相通的,只由一个num来区别。而且它的实现思路与显式数集法相仿,只不过由以位置由中心变成了以候选数为中心而已。首先是以房为单位,把那些只有少于num个候选位置的候选数给选出来,然后利用_get_n_from_m从中任选num个,看他们的并集是否正好是num个位置。

def _helper_hiddenNumberSet( self, housename, num ):

con = list()

for i in range(9):

con.append(dict())

groupBy = getattr( self, '_groupBy%s' %housename )

for n, posset in self._candi_pos.items():

house = groupBy( posset )

for i, items in house.items():

if len(items)<=num:

con[i][n]=set(items)

for i in range(9):

if len(con[i])>=num:

for indices in self._get_n_from_m( num, len(con[i])):

ks = con[i].keys()

nSet = []

uSet = set()

toRemove = []

for index in indices:

nSet.append(ks[index])

uSet = uSet.union(con[i][ks[index]])

if len(uSet)==num:

for pos in uSet:

for j in self._valid_set[pos]:

if j not in nSet:

toRemove.append( (pos[0], pos[1], j) )

if toRemove:

self._changed = True

print 'in %s%d,' %(housename, i),

for n in nSet:

print '%d,' %n,

print 'have only candidate postion',

for n in uSet:

print n,

print

self._removeCandidate(toRemove)

return True

return False

区块删减法基本可以用下面两点来概括:

1. 在某一区块即宫中,如果某个候选数只出现在一行(或者一列中),那么可以将该候选数从该行(或者该列)的其他单元格中删除,因为我们知道,每一个行/列/宫都有且只有一个候选数i,如果该候选数i在某个块中只出现在同一行,那么该行便已经确定在这个块中会有这个候选数了,所以该行的其他单元格就不能再有这个候选数了。

2. 在某一行/列中,如果某个候选数只出现在同个块中,那么可以将该候选数从该块中的其他单元格中删除。

def _regionDeletion( self ):

self._changed = False

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值