第 14 章 排列組合與機率¶
本章重點:
- 用 Python / SymPy 協助處理「有幾種可能?」這類問題。
- 認識階乘、排列、組合,並用 SymPy 計算。
- 建立基本機率模型:樣本空間、事件、等可能模型。
- 練習計算基本機率、條件機率與獨立性。
- 用程式模擬(實驗)來驗證機率計算結果,培養直覺。
- 初步接觸二項分配與 SymPy 的機率工具。
本章適合作為「離散數學+基礎機率」的溫和入門, 同時示範如何用電腦做大量計算與模擬,幫助你檢查自己的推理。
14.1 動機:從日常情境到計數問題¶
日常生活中充滿「有幾種可能?」的問題:
- 手機解鎖圖形有幾種?
- 從 10 個候選人中選出 3 位委員,有幾種選法?
- 抽卡遊戲中,抽到至少一張稀有卡的機率是多少?
這些問題背後的核心其實是兩個:
- 怎麼數(計數)? —— 排列、組合、階乘⋯⋯
- 怎麼算機率? —— 有利情況 / 所有可能情況。
本章要做的事:
- 用 SymPy 幫忙計算各種排列組合數量。
- 用 Python 列出所有可能情況,從「列舉」連結到「公式」。
- 用程式模擬隨機實驗,檢查理論機率是否合理。
14.2 SymPy 的基本組合函數¶
SymPy 中與排列組合相關的基本函數包括:
factorial(n):$n!$(階乘)。binomial(n, k):$\binom{n}{k}$(組合數)。
先來試試看:
import sympy as sp
sp.init_printing()
n, k = sp.symbols('n k', integer=True, nonnegative=True)
fact_5 = sp.factorial(5)
C_5_2 = sp.binomial(5, 2)
fact_5, C_5_2
結果:
- $5! = 5\times 4\times 3\times 2\times 1 = 120$。
- $\binom{5}{2} = 10$,代表從 5 個人中選 2 個的選法數量。
14.3 排列:有順序的安排¶
若有 $n$ 個不同元素,排成一排,每一種不同的排序稱為一種排列。
- 全部排出來的排列數是 $n!$。
- 若只選出其中 $r$ 個排成一排,排列數記為 $P(n,r)$,公式為
$$P(n,r) = n (n-1) (n-2) \cdots (n-r+1) = \frac{n!}{(n-r)!}.$$
在 SymPy 中沒有特別叫 permutation(n, r) 的內建函數,
但可以用 factorial 或 binomial 自己寫:
def P(n, r):
return sp.factorial(n) / sp.factorial(n-r)
P_5_3 = P(5, 3)
P_5_3
這代表從 5 個不同的人中選 3 個排成一排,有 60 種不同的順序。
啟發:用 Python 真的排一排看看¶
我們可以用 itertools.permutations 實際列出所有排列,
確認數量是否為 60(或從小例子開始)。
import itertools
items = ['A', 'B', 'C']
perms = list(itertools.permutations(items))
len(perms), perms
(6,
[('A', 'B', 'C'),
('A', 'C', 'B'),
('B', 'A', 'C'),
('B', 'C', 'A'),
('C', 'A', 'B'),
('C', 'B', 'A')])
可以看到 3 個不同物件的全排列有 $3! = 6$ 種,與理論一致。
14.4 組合:不在乎順序的選法¶
若只關心「選出哪幾個」而不在乎排序,就得到組合。
從 $n$ 個不同物件中選出 $k$ 個的組合數為:
$$\binom{n}{k} = \frac{n!}{k!(n-k)!}.$$
在 SymPy 中用 sp.binomial(n, k):
sp.binomial(5, 3), sp.binomial(10, 2), sp.binomial(52, 5)
最後一個 $\binom{52}{5}$ 是「從一副 52 張牌中選出 5 張牌」的可能數, 也就是一般撲克牌手牌的總數。
啟發:排列 vs 組合的差別¶
以 {A, B, C} 選 2 個為例:
- 排列 $P(3,2)$:AB, BA, AC, CA, BC, CB(6 種)。
- 組合 $\binom{3}{2}$:{A,B}, {A,C}, {B,C}(3 種)。
你可以用 Python 實際列出:
items = ['A', 'B', 'C']
perms_2 = list(itertools.permutations(items, 2))
from itertools import combinations
combs_2 = list(combinations(items, 2))
len(perms_2), perms_2, len(combs_2), combs_2
(6,
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')],
3,
[('A', 'B'), ('A', 'C'), ('B', 'C')])
14.5 啟發性例子一:樂透抽球¶
假設有 42 個號碼球,從中不放回地抽出 6 顆,且順序不重要(就是一般樂透)。
- 總共有多少種可能開獎結果?
- 如果你買 1 組號碼,中獎機率是多少?
用 SymPy:
total_outcomes = sp.binomial(42, 6)
prob_win = 1 / total_outcomes
total_outcomes, sp.nsimplify(prob_win), float(prob_win)
可以看到總組合數極大,中獎機率極低。
這個例子說明:
- 組合自然用來描述「從 n 個中選 k 個」的情境。
- 機率可以用「有利情況 / 所有情況」來定義。
14.6 基本機率:樣本空間與事件¶
在最簡單「等可能」模型中:
- 樣本空間 $\Omega$:所有可能結果的集合。
- 事件 $A$:$\Omega$ 的一個子集合。
- 機率:
$$P(A) = \frac{|A|}{|\Omega|}.$$
例:擲一顆公平骰子一次。
- $\Omega = \{1,2,3,4,5,6\}$。
- $A = \{2,4,6\}$ =「點數為偶數」。
- $P(A) = 3/6 = 1/2$。
用 Python 列出樣本空間:
Omega = list(range(1, 7))
A = [x for x in Omega if x % 2 == 0]
Omega, A, len(A)/len(Omega)
14.7 兩次擲骰子的樣本空間與事件¶
擲兩顆(或一顆骰子連擲兩次),樣本空間可以表示為有序對 $(i,j)$:
$\Omega = \{(i,j) : i = 1..6, j = 1..6\}$,共有 $6\times 6 = 36$ 種結果。
例:事件 $A$ =「點數和為 7」。
先用 Python 列出所有 $(i,j)$,再篩選和為 7 的情況:
Omega = [(i, j) for i in range(1, 7) for j in range(1, 7)]
A = [(i, j) for (i, j) in Omega if i + j == 7]
len(Omega), A, len(A)/len(Omega)
可以看到:和為 7 的組合有 6 種,因此機率為 $6/36 = 1/6$。
14.8 補事件、聯集、交集與容斥原理(簡單版)¶
在等可能模型中:
- 補事件:$A^c$ 代表「不是 A 的所有結果」,$P(A^c) = 1 - P(A)$。
- 聯集:$A \cup B$ 代表「屬於 A 或 B(或兩者)」。
- 交集:$A \cap B$ 代表「同時屬於 A 和 B」。
基本關係:
$$P(A \cup B) = P(A) + P(B) - P(A \cap B).$$
例:擲一顆骰子一次。
- $A$ =「點數是偶數」。
- $B$ =「點數大於等於 4」。
用 Python 實作:
Omega = set(range(1, 7))
A = {x for x in Omega if x % 2 == 0}
B = {x for x in Omega if x >= 4}
A_union_B = A | B
A_inter_B = A & B
A_comp = Omega - A
Omega, A, B, A_union_B, A_inter_B, A_comp, len(A_union_B)/len(Omega)
你可以檢查:
- $P(A) = 3/6$,$P(B) = 3/6$,$P(A \cap B) = 2/6$。
- $P(A \cup B) = 3/6 + 3/6 - 2/6 = 4/6$。
14.9 條件機率與獨立性¶
條件機率 $P(A\mid B)$ 代表在「知道 B 發生」的前提下,A 發生的機率:
$$P(A\mid B) = \frac{P(A \cap B)}{P(B)} \quad (P(B) > 0).$$
兩事件 A, B 若滿足:
$$P(A \cap B) = P(A)P(B),$$
則稱 A, B 獨立。
例:擲一顆骰子一次。
- $A$ =「點數是偶數」。
- $B$ =「點數大於等於 4」。
我們來計算 $P(A\mid B)$ 並檢查 A, B 是否獨立:
Omega = set(range(1, 7))
A = {x for x in Omega if x % 2 == 0}
B = {x for x in Omega if x >= 4}
PA = len(A)/len(Omega)
PB = len(B)/len(Omega)
PAB = len(A & B)/len(Omega)
PAgivenB = PAB / PB
PA, PB, PAB, PAgivenB, PA*PB
結果:
- $P(A) = 1/2$。
- $P(B) = 1/2$。
- $P(A \cap B) = 2/6 = 1/3$。
- $P(A\mid B) = (1/3)/(1/2) = 2/3$。
因為 $P(A \cap B) \ne P(A)P(B)$,所以 A 與 B 不是獨立事件。
14.10 隨機模擬:用 Python 擲硬幣與擲骰子¶
數學機率給出「理論值」, 但我們也可以用電腦模擬大量實驗,看看頻率是否接近機率。
例:擲硬幣,正反各半。
- 理論上 $P(正面) = 1/2$。
- 我們模擬 1000 次,看看正面出現的比例。
import random
def simulate_coin_flips(n_trials=1000):
heads = 0
for _ in range(n_trials):
if random.random() < 0.5: # 0~1 均勻,在 0.5 之下算正面
heads += 1
return heads/n_trials
simulate_coin_flips(1000)
你可以多跑幾次,或把 n_trials 改成 10000、100000,觀察比例如何收斂到 0.5。
同樣地,我們也可以模擬兩顆骰子點數和為 7 的機率:
def simulate_two_dice_sum_7(n_trials=10000):
count = 0
for _ in range(n_trials):
d1 = random.randint(1, 6)
d2 = random.randint(1, 6)
if d1 + d2 == 7:
count += 1
return count/n_trials
simulate_two_dice_sum_7(10000)
理論值是 $1/6 \approx 0.1667$,模擬結果應該接近這個數字。
from sympy.stats import Die, P, E, variance
d = Die('d', 6) # 一顆六面公平骰子
prob_3 = P(d > 3) # 點數大於 3 的機率
expected = E(d) # 期望值
var = variance(d)
prob_3, expected, var
你可以嘗試:
P(d == 6):出現 6 的機率。P(d <= 2):小於等於 2 的機率。E(d**2):點數平方的期望值。
雖然本章不深入「期望值」與「變異數」, 但你可以把它們想成「平均」與「變化程度」的機率版本, 會在之後資料分析章節再連結回來。
14.12 二項分配:多次成功/失敗的模型¶
當我們有一個「成功/失敗」的試驗(例如擲硬幣),且:
- 每次成功的機率為 $p$。
- 試驗之間互相獨立。
連續進行 $n$ 次,成功的次數 $X$ 會服從二項分配:
$$P(X = k) = \binom{n}{k} p^k (1-p)^{n-k}.$$
例:擲一枚不公平硬幣 5 次,每次正面機率為 $p=0.3$。
求正面出現 2 次的機率。
n = 5
k = 2
p = sp.Rational(3, 10) # 0.3
prob_k = sp.binomial(n, k) * p**k * (1-p)**(n-k)
sp.simplify(prob_k), float(prob_k)
我們也可以用 sympy.stats 來計算:
from sympy.stats import Binomial
X = Binomial('X', n, p)
P_X_eq_2 = P(X == 2)
P_X_eq_2
你可以試著算 $P(X = 0)$、$P(X = 1)$,並觀察所有 $k=0..5$ 的機率總和是否為 1。
14.13 本章小結¶
本章你學到了:
- 如何用 SymPy 計算階乘、排列數 $P(n,r)$ 與組合數 $\binom{n}{k}$。
- 在等可能模型中,以「有利情況 / 所有情況」定義機率。
- 補事件、聯集、交集與條件機率的基本觀念。
- 用 Python 列舉樣本空間與事件,並透過模擬(隨機實驗)加深對機率的直觀。
- SymPy.stats 提供了機率變數、期望值與變異數的符號處理。
- 二項分配作為「多次成功/失敗試驗」的基本模型,與排列組合的關係。
這些工具在後續的資料分析、統計與機器學習中都非常常見, 本章的目標是幫你建立一個整體的輪廓,並熟悉 Python / SymPy 如何輔助計算。
14.14 練習題¶
請在本 Notebook 中新增儲存格,試著完成以下練習:
基本排列組合計算
(a) 使用 SymPy 計算 $7!$、$P(7,3)$、$\binom{7}{3}$。
(b) 解釋 $P(7,3)$ 與 $\binom{7}{3}$ 在意義上的差別。
(c) 嘗試設計一個「選班代表」的情境,分別適合用排列與組合來建模。撲克牌問題
一副 52 張的撲克牌,假設不考慮花色順序。
(a) 從中抽出 5 張牌有幾種不同組合?
(b) 假設有 4 張 A(Ace),抽到「剛好一張 A」的 5 張手牌有幾種可能?
(c) 計算抽到「剛好一張 A」的機率。
(提示:用組合數表示「選出 A 的方式 × 選出非 A 的方式」。)骰子與機率
擲兩顆公平骰子,設 $X$ 為兩顆點數的和。
(a) 用 Python 列出所有 36 種 $(i,j)$,統計各種點數和(2~12)的出現次數。
(b) 用程式計算 $P(X=7)$、$P(X\ge 10)$。
(c) 嘗試用理論方式計算這些機率,確認與程式一致。條件機率練習
擲一顆公平骰子一次。
(a) 令 $A$ =「點數為偶數」,$B$ =「點數大於 3」。
計算 $P(A)$、$P(B)$、$P(A\cap B)$、$P(A\mid B)$。
(b) 判斷 A 與 B 是否獨立。
(c) 嘗試再設計一對事件,使它們是獨立的,並驗證 $P(A \cap B) = P(A)P(B)$。隨機模擬 vs 理論機率
(a) 寫一個函數模擬擲三顆骰子,重複 10000 次,估計點數和為 10 的機率。
(b) 嘗試用程式枚舉所有點數組合,計算理論機率,並與模擬結果比較。
(c) 觀察當實驗次數從 1000 增加到 10000、50000 時,估計值如何收斂。二項分配練習
一枚硬幣正面機率為 0.4,連續擲 6 次。
(a) 使用二項分配公式或 SymPy.stats 計算:正面出現 0,1,2,...,6 次的機率。
(b) 檢查所有機率加總是否為 1。
(c) 使用隨機模擬(大量試驗)估計「正面出現恰好 2 次」的機率,並與理論值比較。加分題:機率與期望值的連結
以擲一顆公平骰子為例,令隨機變數 $X$ 為點數。
(a) 寫出 $P(X=k)$ 對於 $k=1..6$。
(b) 用定義 $E[X] = \sum_k k P(X=k)$ 計算期望值(不使用 SymPy.stats)。
(c) 再用 SymPy.stats 的E(d)求出期望值,確認兩者一致。
(d) 思考:如果玩一個「擲骰子,獲得等於點數的獎金」的遊戲, 每玩一次的合理門票價格大約應該是多少?