跳轉到

附錄 C:離散數學與程式實作的速查(Python)#

本書各章的 Python 範例散落各節,本附錄集中提供最常用的十段「即戰力」程式碼,供複習與實驗。

C.1 邏輯運算與真值表(第 1 章)#

import itertools

def truth_table(expr, vars):
    for vals in itertools.product([0,1], repeat=len(vars)):
        env = dict(zip(vars, vals))
        print(env, '->', expr(env))

# 檢驗 De Morgan:not(P and Q) == (not P) or (not Q)
for p, q in itertools.product([0,1], repeat=2):
    assert (not (p and q)) == (not p or not q)

C.2 子集與冪集(第 2 章)#

def powerset(S):
    S = list(S)
    n = len(S)
    return [frozenset(S[i] for i in range(n) if mask>>i & 1)
            for mask in range(2**n)]

print(len(powerset({1,2,3})))  # 8

C.3 排列組合(第 3 章)#

from math import comb, perm
from itertools import combinations, permutations

print(comb(52,5))       # 2,598,960:德州撲克手牌
print(perm(10,3))       # 720

C.4 遞迴與動態規劃(第 4 章)#

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    return n if n < 2 else fib(n-1) + fib(n-2)

def hanoi(n, a='A', b='B', c='C'):
    if n:
        hanoi(n-1, a, c, b)
        print(f"{a} -> {c}")
        hanoi(n-1, b, a, c)

C.5 生成函數(第 4 章)#

# (1+x)^n 係數 = 二項式係數;兌換問題用捲積
from functools import reduce
import numpy as np

def coins_change(coins, upto):
    f = np.zeros(upto+1); f[0] = 1
    for c in coins:                     # 每種硬幣乘上 1+x^c+x^{2c}+...
        for i in range(c, upto+1):
            f[i] += f[i-c]
    return f

print(int(coins_change([1,2,5], 10)[10]))  # 10 元兌法數

C.6 圖與最短路徑(第 5–6 章)#

import heapq

def dijkstra(adj, s):
    dist = {s: 0}
    pq = [(0, s)]
    while pq:
        d, u = heapq.heappop(pq)
        if d > dist.get(u, float('inf')): continue
        for v, w in adj[u]:
            if d + w < dist.get(v, float('inf')):
                dist[v] = d + w
                heapq.heappush(pq, (dist[v], v))
    return dist

# 最小生成樹(Kruskal)
def kruskal(n, edges):
    parent = list(range(n))
    def find(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]; x = parent[x]
        return x
    mst = []
    for u, v, w in sorted(edges, key=lambda e: e[2]):
        if find(u) != find(v):
            parent[find(u)] = find(v)
            mst.append((u, v, w))
    return mst

C.7 圖著色與平面性(第 6 章)#

def is_bipartite(adj, n):
    color = [None]*n
    for s in range(n):
        if color[s] is None:
            color[s] = 0
            stack = [s]
            while stack:
                u = stack.pop()
                for v in adj[u]:
                    if color[v] is None:
                        color[v] = color[u]^1; stack.append(v)
                    elif color[v] == color[u]:
                        return False
    return True

C.8 模運算與 RSA(第 7 章)#

def egcd(a, b):
    if b == 0: return (a, 1, 0)
    g, x, y = egcd(b, a % b)
    return (g, y, x - (a//b)*y)

def modinv(a, n):
    g, x, _ = egcd(a % n, n)
    assert g == 1
    return x % n

def is_prime(n):
    if n < 2: return False
    i = 2
    while i*i <= n:
        if n % i == 0: return False
        i += 1
    return True

# pow(base, exp, mod) 內建即快速冪,RSA 核心一行
m, e, d, N = 42, 65537, 12345, 97*101
assert pow(pow(m, e, N), d, N) == m

C.9 群的階與 Burnside(第 8 章)#

def order(g, n):
    k, cur = 1, g % n
    while cur != 1:
        cur = cur * g % n; k += 1
    return k

print(order(3, 7))   # 6:3 是模 7 的原根

C.10 有限自動機(第 8 章)#

def run_dfa(delta, start, accept, s):
    q = start
    for ch in s:
        q = delta[(q, ch)]
    return q in accept

# 接受含偶數個 a 的字串
delta = {(0,'a'):1, (0,'b'):0, (1,'a'):0, (1,'b'):1}
assert run_dfa(delta, 0, {0}, "abba")
assert not run_dfa(delta, 0, {0}, "ab")

小結#

這些片段共同展示了本書的核心論點:離散數學的每一個抽象結構(邏輯、集合、遞迴、圖、群、自動機),都能在幾行程式碼中「活起來」。建議讀者把每段程式跑一遍、改參數、破壞它、再修好——這是最快的理解方式。