📅  最后修改于: 2023-12-03 15:12:37.830000             🧑  作者: Mango
本篇文章是针对 GATE CS 2021 考试中设置 2 的第 3 个问题而写的。该问题涉及 针对给定的布尔函数生成多项式 或 给定布尔函数的所有合取范式,我们需要完整地介绍这两种情况,以及相应的解法和实现方案。
在进一步介绍该问题的解法之前,我们需要先了解一些相关的概念。
首先,什么是布尔函数?布尔函数是表示从布尔域 {0, 1}(也可看作逻辑值的集合 {false, true})到布尔域 {0, 1} 的函数。可以将其看作逻辑运算的一种抽象表示,例如 AND、OR、NOT 等。例如,对于 3 个布尔变量 x、y、z,其与运算可以表示为布尔函数 f(x, y, z) = x ∧ y ∧ z。
其次,什么是多项式?在通常情况下,多项式指的是由系数和变量的乘积所构成的一种表达式。例如,2x^2 + 3xy - 5y^2 就是一个多项式。而在布尔代数中,多项式可以看作仅包含 AND 和 OR 两种逻辑运算的表达式。例如,x ∧ y ∧ z + x ∧ y ∧ ¬z 就是一个布尔多项式。
最后,什么是提取析取范式和合取范式?提取析取范式和合取范式分别是布尔函数的两种标准形式。在提取析取范式中,布尔函数被表示为多项式的 OR 合取式。例如,对于上面提到的三个变量的与运算,其提取析取范式可以表示为 f(x, y, z) = (x ∧ y ∧ z) ∨ (x ∧ y ∧ ¬z) ∨ (x ∧ ¬y ∧ z) ∨ (x ∧ ¬y ∧ ¬z) ∨ (¬x ∧ y ∧ z) ∨ (¬x ∧ y ∧ ¬z) ∨ (¬x ∧ ¬y ∧ z)。而在合取范式中,布尔函数被表示为多项式的 AND 合取式。例如,对于与运算,其合取范式可以表示为 f(x, y, z) = (x ∨ ¬y ∨ ¬z) ∧ (¬x ∨ y ∨ ¬z) ∧ (¬x ∨ ¬y ∨ z)。
在给定布尔函数之后,生成其多项式的方法有多种。这里介绍两种常见方法和一种更加高级的方法。
第一种方法是通过将布尔函数转化为真值表,然后基于真值表得出布尔多项式。具体操作步骤如下:
第二种方法是通过 Karnaugh 地图(Karnaugh map)得出布尔多项式。这种方法应用于布尔变量不多的情况下较为有效。具体操作步骤如下:
第三种方法是使用 Quine-McCluskey 算法。Quine-McCluskey 算法是一种较为复杂的方法,但具有较高的计算效率。其步骤如下:
代码示例分别针对上述三种方法,实现了多项式得出的过程。代码使用 Python 语言编写,仅作为学习用途,具体的输入方式和输出方式可按照实际需求进行修改。
# 生成多项式 - 真值表
truth_map = [[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [0, 1, 1, 1], [1, 0, 0, 1], [1, 0, 1, 0], [1, 1, 0, 0], [1, 1, 1, 1]]
variables = ['x1', 'x2', 'x3']
def gen_func(truth_map: list, variables: list):
res = []
for row in truth_map:
temp = []
for i, ele in enumerate(row):
temp.append((variables[i] if ele else f'¬{variables[i]}'))
res.append(temp)
multi = []
for row in res:
multi.append(' ∧ '.join(row))
return ' ∨ '.join(multi)
print(gen_func(truth_map, variables))
# 生成多项式 - Karnaugh 地图
from typing import List
def get_karnaugh_chunks(truth_map: List[List[int]]):
chunk = {0: [], 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: [], 13: [], 14: [], 15: []}
for i, row in enumerate(truth_map):
if i % 4 == 0:
chunk[i].append(row[0])
chunk[i].append(row[1])
chunk[i+1].append(row[2])
chunk[i+1].append(row[3])
else:
chunk[i-1].append(row[0])
chunk[i-1].append(row[1])
chunk[i].append(row[2])
chunk[i].append(row[3])
return chunk
kmap = get_karnaugh_chunks(truth_map)
def gen_func_kmap(chunk: dict) -> str:
res = []
for k, v in chunk.items():
if all(map(lambda x: x == 1, v)):
x = k // 4
y = k % 4
res.append(f"x{'' if x == 0 else x+1}{'¯' if y == 0 else '' if y == 1 else '¯'}")
return ' ∨ '.join(res)
print(gen_func_kmap(kmap))
# 生成多项式 - Quine-McCluskey 算法
class QuineMcCluskey:
def __init__(self, variables: List[str], truth_map: List[List[int]]):
self.variables = variables
self.truth_map = truth_map
self.minterms = []
self.prime_implicants = {}
self.essential_primes = []
for i, row in enumerate(truth_map):
if sum(row) > 0:
self.minterms.append((row, set([i])))
def get_distinct_digits(self, x, y):
"""
得到两个 minterm 之间不同的位置
"""
res = []
for i, digit in enumerate(x):
if digit != y[i]:
res.append(i)
return res
def create_table(self):
"""
构造出 Quine-McCluskey 图表
"""
table = {}
for minterm in self.minterms:
for digit in minterm[0]:
if digit not in table:
table[digit] = []
table[digit].append(minterm[1])
return table
def find_ones(self, value: int):
"""
计算一个数字二进制表示中 1 的个数
"""
count = 0
while (value > 0):
count += value % 2
value //= 2
return count
def get_implicants(self, ones: List[int]) -> List[str]:
"""
根据 1 的位置得到可能的 implicant
"""
implicants = []
for i in range(len(ones)):
for j in range(i+1, len(ones)):
digits = self.get_distinct_digits(bin(ones[i])[2:].zfill(len(self.variables)), bin(ones[j])[2:].zfill(len(self.variables)))
if len(digits) == 1:
implicants.append((ones[i], ones[j], digits[0]))
return implicants
def simplify(self):
"""
计算出最终的 prime implicant
"""
table = self.create_table()
# 构造出每一个 implicant 的代表列表
implicant_list = []
for i in range(len(self.variables)+1):
ones = [int(k) for k, v in table.items() if len(v) == 2**i]
implicants = self.get_implicants(ones)
implicant_list += implicants
# 基于 implicant,构造出 prime composite implicant 表
prime_implicant_table = {}
for implicant in implicant_list:
temp_str = bin(implicant[0] | implicant[1])[2:].zfill(len(self.variables))
if len(temp_str) not in prime_implicant_table:
prime_implicant_table[len(temp_str)] = []
prime_implicant_table[len(temp_str)].append((bin(implicant[0])[2:].zfill(len(self.variables)), bin(implicant[1])[2:].zfill(len(self.variables)), implicant[2]))
# 根据 prime composite implicant 表,计算出所有的 prime implicants
needed = set([i for i in range(len(self.minterms))])
while len(needed) > 0:
misses = set()
temp_prime_implicant = {}
temp_essential = []
for key, value in prime_implicant_table.items():
for candidate in value:
done = True
for i in candidate[:2]:
if not(self.minterms[i][1].issubset(needed)):
done = False
if done:
if key not in temp_prime_implicant:
temp_prime_implicant[key] = []
temp_prime_implicant[key].append(candidate)
for i in candidate[:2]:
needed = needed - self.minterms[i][1]
if len(candidate) == 3:
temp_essential.append(candidate)
if len(temp_prime_implicant) == 0:
missed = []
total = set()
for i in needed:
s = bin(i)[2:].zfill(len(self.variables))
new_row = set()
for j in s:
if j == '0':
new_row.add(0)
elif j == '1':
new_row.add(1)
temp = []
for k, v in enumerate(new_row):
if v == 0:
temp.append(f' ¬{self.variables[k]}')
else:
temp.append(self.variables[k])
missed.append(' ∧ '.join(temp))
total |= new_row
temp_essential.append((0, 0, list(total-started)[0]))
break
else:
self.prime_implicants.update(temp_prime_implicant)
self.essential_primes += temp_essential
return self.prime_implicants, self.essential_primes
def gen_func_quine(self):
self.simplify()
primes = self.prime_implicants
essent = self.essential_primes
res = []
for key, value in primes.items():
for mv in value:
temp = []
for i, ele in enumerate(mv[:2]):
if ele != 0:
row = []
for j in bin(ele)[2:].zfill(len(self.variables)):
if j == '0':
row.append(f'¬{self.variables.pop(0)}')
elif j == '1':
row.append(self.variables.pop(0))
temp.append(' ∧ '.join(row))
res.append(' ∨ '.join(temp))
return ' ∨ '.join(res)
QM = QuineMcCluskey(variables, truth_map)
print(QM.gen_func_quine())
对于给定布尔函数,其所有的合取范式可以通过以下步骤得到:
例如,对于上面提到的三个变量的与运算,其提取析取范式可以表示为 f(x, y, z) = (x ∧ y ∧ z) ∨ (x ∧ y ∧ ¬z) ∨ (x ∧ ¬y ∧ z) ∨ (x ∧ ¬y ∧ ¬z) ∨ (¬x ∧ y ∧ z) ∨ (¬x ∧ y ∧ ¬z) ∨ (¬x ∧ ¬y ∧ z)。而将其 AND 和 OR 的逻辑运算交换,可以得到合取范式 f(x, y, z) = (x ∨ ¬y ∨ ¬z) ∧ (¬x ∨ y ∨ ¬z) ∧ (¬x ∨ ¬y ∨ z)。
代码示例使用 Python 语言编写,基于第一节中已经实现的函数 gen_func_kmap 和 gen_func_quine,分别使用提取析取范式和 Quine-McCluskey 算法来生成合取范式。代码的输入变量与上一节中相同。
# 生成合取范式 - 提取析取范式
def extract_to_conjunct(truth_map: List[List[int]], variables: List[str]) -> str:
kmap = get_karnaugh_chunks(truth_map)
res = []
for k, v in kmap.items():
if all(map(lambda x: x == 1, v)):
x = k // 4
y = k % 4
temp = []
for i, var in enumerate(variables):
if i == x:
temp.append(var)
else:
temp.append(f'¬{var}')
res.append(' ∧ '.join(temp))
return ' ∧ '.join(res)
print(extract_to_conjunct(truth_map, variables))
# 生成合取范式 - Quine-McCluskey 算法
QM = QuineMcCluskey(variables, truth_map)
pn, _ = QM.simplify()
res = []
for key, value in pn.items():
for mv in value:
temp = []
for i, ele in enumerate(mv[:2]):
if ele != 0:
row = []
for j in bin(ele)[2:].zfill(len(variables)):
if j == '0':
row.append(f'¬{variables.pop(0)}')
elif j == '1':
row.append(variables.pop(0))
temp.append(' ∨ '.join(row))
res.append(' ∧ '.join(temp))
print(' ∧ '.join(res))
本篇文章介绍了如何针对给定的布尔函数生成多项式,以及如何得到该布尔函数的所有合取范式。其中,多项式的生成可以通过真值表、Karnaugh 地图或 Quine-McCluskey 算法来实现;而合取范式的生成则可以通过将提取析取范式中 AND 和 OR 逻辑运算交换,或使用 Quine-McCluskey 算法来实现。开发者可以根据实际情况选择相应的方法和工具进行实现和优化。