化简逻辑表达式时使用的卡诺图,如果通过手工化简,在变量较多时会变得难以处理,容易出错。故笔者编写了如下的自动化简卡诺图脚本,让大家脱离手工化简的苦海。
使用方法:执行后调用AutoKarnaugh(item,bitlen)函数 item:表示使逻辑函数取真的所有最小项的编号的list bitlen:表示逻辑函数变量个数,返回此函数的最简表达式 '-X'表示X取反
如:
>>> AutoKarnaugh([0,1,2,3,4],4)
'-A-C-D+-A-B'
表示此函数最简表达式为
算法思路主要是将卡诺图中的找相邻项暴力处理为遍历所有最小项,找到所有只有一个变量不同的项,进而避免图表的处理。写完后发现这个算法其实已经有人发现过了,叫Q-M算法,也体验到了一把跟先辈思路对上了的快乐。
def greyCode(n):
if n == 1:
return ['0','1']
retv=greyCode(n-1)
retv=retv+list(reversed(retv))
l=2**(n-1)
for i in range(l):
retv[i]='0'+retv[i]
retv[l+i]='1'+retv[l+i]
return retv
def code2int(code):
assert isinstance(code, str)
ans=0
cnt=0
for x in reversed(code):
ans=ans+(2**cnt if x == '1' else 0)
cnt+=1
return ans
def getInput(lis,bitlen):
assert lis and bitlen<26
l=len(lis)
greyDict={}
codes=[]
for x in greyCode(bitlen):
greyDict[code2int(x)]=x
for x in lis:
codes.append(greyDict[x])
return codes
def isNeighbor(a,b):
assert len(a)==len(b)
l=len(a)
cnt=0
ass=None
for i in range(l):
if a[i]=='x' or b[i]=='x':
if not a[i]==b[i]:
return None
continue
if a[i]!=b[i]:
cnt+=1
ass=i
if cnt>1:
return None
return ass
def outputCode(code):
alphaBeta='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
cnt=0
ans=''
for x in code:
if x=='x':
cnt+=1
continue
if x=='1':
ans+=alphaBeta[cnt]
if x=='0':
ans+='-'+alphaBeta[cnt]
cnt+=1
return ans
def simpify(codes):
def firstSim(codes):
if not codes:
return []
l=len(codes)
vis=[False for i in range(l)]
newcodes=[]
for i in range(l):
for j in range(l):
k=isNeighbor(codes[i], codes[j])
if k is not None:
vis[i]=True
vis[j]=True
newcodes.append(codes[i][0:k]+'x'+codes[i][k+1:])
retv=[]
for i in range(l):
if not vis[i]:
retv.append(codes[i])
return list(set(retv+firstSim(list(set(newcodes)))))
def secondSim(simpifiedCodes):
def getCovereds(code):
retv=[]
if not 'x' in code:
return [code]
for i in range(len(code)):
if code[i]=='x':
retv+=getCovereds(code[:i]+'0'+code[i+1:])+getCovereds(code[:i]+'1'+code[i+1:])
return retv
coverList=[getCovereds(code) for code in simpifiedCodes]
isPrime=[False for x in simpifiedCodes] #标记每个蕴含项是否是实质蕴含项
retv=[] #最终返回的化简项
for i in range(len(simpifiedCodes)): #找出所有的实质蕴含项
othercovered=sum(coverList[:i]+coverList[i+1:],[])
if any([x not in othercovered for x in coverList[i]]): #是实质蕴含项
retv.append(simpifiedCodes[i]) #加入返回值
isPrime[i]=True
primecover=[]
for i in range(len(isPrime)):
if isPrime[i]:
primecover+=coverList[i] #得出所有被实质蕴含项覆盖的最小项
notcovered=[]
for x in codes:
if x not in primecover:
notcovered.append(x)#得出所有没有被实质蕴含项覆盖的最小项
nprimecodes=[]#所有非实质蕴含项的质蕴含项的编号
for i in range(len(isPrime)):
if not isPrime[i]:
nprimecodes.append(i)
def genSubset(lis):
subset = [[]]
for x in lis:
subset.extend([item + [x] for item in subset])
return sorted(subset,key=lambda a:len(a))
for nprime in genSubset(nprimecodes):
cover=[]
for x in nprime:
cover+=coverList[x]
if all([item in cover for item in notcovered]):
retv+=[simpifiedCodes[x] for x in nprime]
return retv
return secondSim(firstSim(codes))
def convertcodes(codes):
ans=''
for x in codes:
ans=ans+outputCode(x)+'+'
ans=ans[:-1]
return ans
def AutoKarnaugh(items,bitlen):
'''item:表示使逻辑函数取真的所有最小项的编号的list bitlen:表示逻辑函数变量个数,返回此函数的最简表达式 '-X'表示X取反'''
return convertcodes(simpify(getInput(items, bitlen)))