📜  门| GATE CS 2021 |设置 2 |问题 3(1)

📅  最后修改于: 2023-12-03 15:12:37.830000             🧑  作者: Mango

门 | GATE CS 2021 | 设置 2 | 问题 3

介绍

本篇文章是针对 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)。

解法

在给定布尔函数之后,生成其多项式的方法有多种。这里介绍两种常见方法和一种更加高级的方法。

第一种方法是通过将布尔函数转化为真值表,然后基于真值表得出布尔多项式。具体操作步骤如下:

  1. 根据变量数目确定真值表的行数。
  2. 根据布尔函数定义填充真值表中的每一行。
  3. 根据每一行得出布尔多项式中各个子式的取值情况。
  4. 将所有子式取值为真的项相加,即得到最终的布尔多项式。

第二种方法是通过 Karnaugh 地图(Karnaugh map)得出布尔多项式。这种方法应用于布尔变量不多的情况下较为有效。具体操作步骤如下:

  1. 根据变量数目将 Karnaugh 地图分为不同的块,每个块对应一个布尔子式。
  2. 在每个块中标记出使布尔函数取值为真的行的位置。
  3. 依次将相邻的块进行合并,得到每个组合对应的布尔子式。
  4. 将所有布尔子式进行 AND 运算,即得到布尔多项式。

第三种方法是使用 Quine-McCluskey 算法。Quine-McCluskey 算法是一种较为复杂的方法,但具有较高的计算效率。其步骤如下:

  1. 将输入的布尔函数转化为 minterm 的形式。
  2. 对于所有的 minterm,将其按照 1 的个数分为不同的组。
  3. 利用相邻组之间的卡诺图(Karnaugh map)来求解最小关键词,并将其保存在一张表中。
  4. 根据上一步得到的表来组合所有的关键词,获得最终的多项式形式。
代码示例

代码示例分别针对上述三种方法,实现了多项式得出的过程。代码使用 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())
给定布尔函数的所有合取范式
解法

对于给定布尔函数,其所有的合取范式可以通过以下步骤得到:

  1. 将布尔函数转换为其提取析取范式。
  2. 将提取析取范式中 AND 和 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 和 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 算法来实现。开发者可以根据实际情况选择相应的方法和工具进行实现和优化。