跳轉到

2.1 布林代數與真值表#

概念說明#

布林代數(Boolean Algebra)由英國數學家 George Boole 於 19 世紀提出,是數位電路設計的數學基礎。布林變數只有兩個值:0(假)與 1(真),正好對應電路中的低電位與高電位。三個基本運算是:

  • AND(與,記號 · 或省略):全部為 1 結果才為 1
  • OR(或,記號 +):任一為 1 結果為 1
  • NOT(非,記號 ¬ 或 '):取反

在此之上可以組合出 NAND、NOR、XOR(互斥或)等複合運算。布林運算中最重要的兩條定律源自德摩根(De Morgan):

  • ¬(A·B) = ¬A + ¬B
  • ¬(A+B) = ¬A · ¬B

德摩根定律是 EDA 的重要工具:NAND 與 NOR 閘在實體上比 AND/OR 閘面積更小、速度更快,所以合成工具會利用德摩根定律大量做 NAND/NOR 重寫。

其他重要恆等式包括:

定律 形式
分配律 A·(B+C) = A·B + A·C;A + B·C = (A+B)·(A+C)
吸收律 A + A·B = A;A·(A+B) = A
合併律 A·B + A·¬B = A
補充律 A + ¬A = 1;A · ¬A = 0
冪等律 A + A = A;A · A = A

真值表(Truth Table) 是布林函數最直接的表示法:對 n 個輸入的所有 2ⁿ 種組合,逐一列出輸出值。真值表的優點是「完備而無歧義」,任何函數對應唯一真值表;缺點是隨變數數呈指數成長——20 個輸入就有一百萬列,因此大規模設計必須改用其他表示法(如 BDD,見 3.2 節)。

兩個關鍵的標準形式:

  • 最小項展開(Sum of Products, SOP):把函數寫成一列 AND 項的 OR,每個 AND 項(最小項,minterm)對應一個輸出為 1 的輸入組合。
  • 最大項展開(Product of Sums, POS):一列 OR 項的 AND,對應輸出為 0 的組合。

SOP 或 POS 都是「兩層邏輯」:輸入經過一層 AND 閘、再一層 OR 閘。兩層邏輯的好處是延遲可預測,缺點是可能浪費閘數;多級優化將在 3.1 節討論。

演算法步驟:由真值表產生 SOP 並化簡#

  1. 列出函數 f 的真值表(0 ~ 2ⁿ−1 共 2ⁿ 列)。
  2. 找出所有 f = 1 的列,將每列轉成最小項:輸入為 1 用原變數、為 0 用取反變數,變數間做 AND。
  3. 把所有最小項用 OR 連接,得到標準 SOP。
  4. 反覆套用布定律(合併 A·B+A·¬B=A、吸收)化簡,直到不再縮短。

Python 程式碼範例#

下面的程式把一個函數轉成標準 SOP,並用「字面值集合」表示積項,實作合併與吸收化簡:

from itertools import product

def truth_table(nvars, func):
    rows = []
    for bits in product([0, 1], repeat=nvars):
        rows.append(bits + (int(func(*bits)),))
    return rows

def sop_from_truth_table(nvars, func):
    names = [chr(ord('a') + i) for i in range(nvars)]
    terms = []
    for bits in product([0, 1], repeat=nvars):
        if func(*bits):
            terms.append(tuple(v if b else "¬" + v
                               for v, b in zip(names, bits)))
    return terms

def shrink(term):
    """把 '¬a' 正規化為 ('a', 0),方便比較"""
    return frozenset((l.lstrip("¬"), 0 if l.startswith("¬") else 1)
                     for l in term)

def simplify(nvars, minterms):
    """合併律 + 吸收律的兩層 SOP 化簡"""
    cubes = [shrink(t) for t in minterms]
    # 步驟1:合併——兩個積項只在同一變數上取值相反且其餘相同
    changed = True
    while changed:
        changed = False
        new, used = set(), [False] * len(cubes)
        for i, c1 in enumerate(cubes):
            for j, c2 in enumerate(cubes):
                if i < j and len(c1 ^ c2) == 1 and len(c1) == len(c2):
                    used[i] = used[j] = True
        # 將差一個「相反字面值」的對合併:直接丟掉那兩個對立方
        merged_map = {}
        for i, c1 in enumerate(cubes):
            for j, c2 in enumerate(cubes):
                if i < j and len(c1) == len(c2):
                    diffs = [(v, a, b) for (v, a) in c1
                             for (v2, b) in c2 if v == v2 and a != b]
                    if len(diffs) == 1 and c1 - {diffs[0][:2]} == c2 - {(diffs[0][0], diffs[0][2])}:
                        m = c1 - {diffs[0][:2], (diffs[0][0], diffs[0][2])}
                        new.add(frozenset(m))
                        used[i] = used[j] = True
        for i, u in enumerate(used):
            if not u:
                new.add(cubes[i])
        if new != set(cubes):
            changed = True
        cubes = list(new)
    # 步驟2:吸收——若 A ⊇ B(A 的約束更少)則 A 是多餘的
    final = [c for c in cubes if not any(
        c != d and {v for v, _ in d} - {v for v, _ in c} == set()
        and len(d) < len(c) for d in cubes)]
    return final

def pretty_term(c):
    return "·".join(lit + v for v, lit in sorted(c))

data = [("f(a,b) = ab + ab'(可合併為 a)", 2, lambda a, b: a and (b or not b)),
        ("g(a,b,c) = ab + bc", 3, lambda a, b, c: (a and b) or (b and c))]

for name, n, f in data:
    print("==", name)
    print("真值表:")
    for row in truth_table(n, f):
        print("  ", row)
    mints = sop_from_truth_table(n, f)
    print("標準SOP:", " + ".join("·".join(t) for t in mints))
    print("化簡後:", " + ".join(sorted(pretty_term(c) for c in simplify(n, mints))),
          "\n")

執行輸出:

== f(a,b) = ab + ab'(可合併為 a)
真值表:
   (0, 0, 0)
   (0, 1, 0)
   (1, 0, 1)
   (1, 1, 1)
標準SOP: a·b + a·¬b
化簡後: a

可見 ab + ab' 經合併律化為單一變數 a——電路上從兩個 AND 閘加一個 OR 閘,變成零閘(直接接線),這正是邏輯優化的威力。

複雜度分析#

  • 真值表列數:2ⁿ,n 為輸入變數數,屬指數級。
  • 標準 SOP 的最小項數最壞為 2ⁿ⁻¹(如奇rey函數)。
  • 上面的合併-吸收迴圈最壞 O(3ⁿ·n)(中介積項可能很多),工業上用 Quine-McCluskey 或 Espresso 啟發式(見 2.2、2.3 節)。

練習#

  1. 將函數 h(a,b,c) = a + b·c 展開成標準 POS(最大項展開),並用程式驗證其真值表與原函數相同。
  2. 修改 simplify,在吸收步驟中改為「刪除被包含者」的寫法,並用 h(a,b,c) = ab + abd + ac 測試是否化簡為 a(b+c)。
  3. 用德摩根定律將 y = ¬(a·b) + ¬c 改寫成只用 NAND 閘表示的運算式,寫出每一步恆等變換。