Q-M算法:算法思路主要是将卡诺图中的找相邻项暴力处理为遍历所有最小项,找到所有只有一个变量不同的项,进而避免图表的处理。
如果算法有错误欢迎留言,我会及时修改。
下面代码将用Q-M算法实现:
下面代码提供3个主函数:
def logic_random_mini(n):随机生成一个n个逻辑变量的真值表,真值表由,使逻辑函数取真的所有最小项的编号组成的列表,描述。
def logic_func_to_mini(mu):将一个书面逻辑函数转化成真值表。比如“-A+B+C-B”,除‘-’和‘+’外,所有符号(包括汉字)都被视作逻辑函数中出现的变量名,‘-’表示对右边第一个的变量的布尔值取反 def logic_mini_to_simplify_func(mini, key):根据真值表生成最简洁的书面逻辑函数,输出形式与第二个函数的输入形式一致。
Q-M融合是指:两个项只有一个逻辑变量的取值不同,就可以合并成一个项。新的项具体为老的项的那个相互不同的逻辑变量取‘-’,也就是随便0或1
然后这整段代码运行效果是:
最开始等待一次键盘输入,如果输入了一个数字,则随机随机生成一个n个逻辑变量的真值表,接着进行求解。否则根据输入的字符串生成真值表,接着求解。
求解过程中,每一轮的Q-M融合循环中。程序会打印出有资格参加下一轮Q-M融合循环的项,每一轮的Q-M融合循环的间隙,程序也会打印剩下的还有可能Q-M融合的项的个数,和不可能再Q-M融合的项的个数。
最后输出最简洁的书面逻辑函数。
程序结束
import random
# logic_random_mini()用于随机生成一个n个逻辑变量的真值表,真值表由,使逻辑函数取真的所有最小项的编号组成的列表,描述。
def logic_random_mini(n): # n表示逻辑函数变量个数
ans = []
for i in range(2 ** n):
if random.random() < 0.5:
ans.append(i)
st = ord('A')
ans1 = []
for i in range(n):
ans1.append(chr(st))
st += 1
return ans, ans1 # ans:使逻辑函数取真的所有最小项的编号组成的列表,ans1:顺序排列的变量名组成的列表,这里设定从字母表里按顺序取。
# logic_func_to_mini()用于把书面的逻辑函数,转化成真值表,并用使逻辑函数取真的所有最小项的编号的列表来表示。并记录逻辑函数中出现的变量名。
def logic_func_to_mini(mu): # mu:书面的逻辑函数字符串,比如“-A+B+C-B”,除‘-’和‘+’外,所有符号都被视作逻辑函数中出现的变量名,‘-’表示对右边第一个的变量的布尔值取反
key = ['-', '+']
for i in mu:
if i not in key:
key.append(i)
key = key[2:]
val = {}
for i in key:
val[i] = False
n = len(key)
p = 2 ** n
ans = []
def calcu(): # 以目前val中储存的所有逻辑变量的取值,计算mu描述的逻辑函数的值,并返回
pre = False
new = True
gv = False
for i in mu:
if i == '-':
gv = True
elif i == '+':
pre = pre or new
new = True
else:
if gv:
new = not (val[i]) and new
gv = False
else:
new = val[i] and new
return pre or new
def step(): # 按2进制步进的形式改变所有逻辑变量的取值,如“0000’-->'1000'-->'0100'-->'1100'-->'0010'-->-->--->'1111'(最终)
def jj(a):
if a >= n:
return
if val[key[a]]:
val[key[a]] = False
jj(a + 1)
else:
val[key[a]] = True
jj(0)
def load(): # 将当前val中储存的所有的逻辑变量的取值情况,转化成对应的最小项的序号,储存进ans中。
pp = 0
for i in range(n - 1, -1, -1):
pp *= 2
if val[key[i]]:
pp += 1
ans.append(pp)
while p > 0:
if calcu():
load()
step()
p -= 1
return ans, key # ans:使逻辑函数取真的所有最小项的编号组成的列表,ans1:顺序排列的变量名组成的列表。
# logic_mini_to_simplify_func()用于将一个使逻辑函数取真的所有最小项的编号的列表和一个变量名列表,还原成最简洁的书面逻辑函数。恒为0将返回‘_0_',恒为1将返回‘_1_'
def logic_mini_to_simplify_func(mini, key): # mini:使逻辑函数取真的所有最小项的编号的列表,key:变量名列表
ku = []
n = len(key)
def mini_to_num(a): # 将最小项的编号转化成真值表变量的0或1,用列表表示。
rr = []
b = n
while b > 0:
if a % 2 == 1:
rr.append(True)
else:
rr.append(False)
a = a // 2
b -= 1
return rr
for i in mini: # 将mini转化成二维的bool类型的数组,来表示所有使逻辑函数取真的变量的取值情况。保存在ku中。
ku.append(mini_to_num(i))
def circel(): # 使ku中的项,按Q-M法合并。(不懂Q-M法的,百度一下)
def check(i, j): # 检查ku中第i个和第j个项,是否只有一个变量不同。是的话,返回唯一不同的变量下表。不是的话,返回-1.
ord = -1
for k in range(n):
if ku[i][k] == ku[j][k]:
continue
else:
if ord >= 0:
return -1
else:
ord = k
return ord
lp = len(ku)
last = [True for i in range(lp)]
oih = []
uh = False
for i in range(lp - 1): # 将ku中的所有项相互检查是否只有一个变量不一样,是的话将只有一个变量不一样两个项融合后的结果不重复地保存在oih中。(这是程序主要的时间花费,这里可以设置成并行运行,或者彻底改变合并筛查算法,来提高效率。本人写的是最低级最暴力的遍历,最浪费时间。)
oij = True
for j in range(i + 1, lp):
op = check(i, j)
if op == -1:
continue
else:
last[i] = False
last[j] = False
jiji = ku[i].copy()
jiji[op] = '-'
if jiji not in oih:
oih.append(jiji)
print(jiji, end='', flush=True)
oij = False
if oij:
pass
else:
uh = True
for i in range(lp):
if last[i]:
oldlady.append(ku[i]) # 将没人要的项保存在oldlady中,不再参与循环,因为错过了同龄人,就不可能有人要了。
print(ku[i], end='', flush=True)
ku.clear()
ku.extend(oih) # 将oih取代原来的ku,准备新的一轮
return uh # 如果一次circle()没有任何项能够融合,说明已经化简到最简洁。返回False,打破循环。
def read(zu): # 将ku中一个项,根据key中变量的顺序,转化成书面形式。
ret = ''
for i in range(n):
if zu[i] == '-':
continue
elif zu[i] == 1:
ret = ret + key[i]
else:
ret = ret + '-' + key[i]
return ret
oldlady = []
while circel(): # 使ku中的项,按Q-M法合并。(不懂Q-M法的,百度一下)
print()
print(len(ku), len(oldlady), sep='+')
oldlady.extend(ku)
ans = ""
for i in oldlady: # 将剩下的无法融合的项,即最终答案,转化为书面形式输出
ans = ans + read(i) + '+'
print("\n")
if ans[:-1] == '':
return "_1_"
return ans[:-1] # 输出最简洁书面逻辑函数
pp = input()
try:
a, b = logic_random_mini(int(pp))
except ValueError:
a, b = logic_func_to_mini(pp)
print(a, b)
print(logic_mini_to_simplify_func(a, b))