前 300 年 — 《九章算術》聯立方程#
案件摘要#
大約在西漢時期(一般認為成書於公元前一世紀,內容可追溯到更早的先秦),中國的《九章算術》第八章「方程」記載了以算籌排成長方陣、用「損益」消去法求解聯立一次方程組的完整方法,並系統性地引入正負數運算法則。這是世界上最早對線性聯立方程組與消去法的系統性記載——比 Gauss 的同名方法早了近兩千年。本案追查:遠古的中國算學家,如何在沒有代數記號的世界裡,破獲了「多未知數」的懸案。
前因 -- 為什麼會有這個案子#
- 田畝與稅收:秦漢時期的國家治理需要大量計算——田畝測量、糧穀交換、按戶徵稅、勞役分攤。這些實務問題經常同時涉及好幾種穀物、好幾個未知量,一個問題就是一組聯立方程。
- 算籌記數:當時沒有符號代數,計算靠擺弄小竹棍(算籌)。要表示「上等禾三捆、中等禾兩捆、下等禾一捆,共收穫三十九斗」這種條件,就把各捆的數字排成一列;把幾個條件疊起來,就自然形成一個「方陣」——「方程」二字,本義就是「並而程之」,把幾列數字並排成一個方形格式。
- 問題樣本(方程章第一題):今有上禾三秉,中禾二秉,下禾一秉,實三十九斗;上禾二秉,中禾三秉,下禾一秉,實三十四斗;上禾一秉,中禾二秉,下禾三秉,實二十六斗。問上、中、下禾實一秉各幾何? 譯成現代記號:
- 古本《九章》原只有題目與答案,方法藏在劉徽(公元 263 年)的註裡——要破案,得靠這位古代偵探留下的證詞。
線索與推理 -- 數學式、程式、理論#
線索一:籌算方陣——文字化的矩陣#
《九章》把上題的條件用算籌排成右高左低的方陣(直行書寫,右為第一列):
這正是一個增廣矩陣的「豎排版本」——每一行是一個方程的係數加常數項。古人沒有矩陣之名,卻有矩陣之實。
線索二:「損益」消去法——Gaussian elimination 的遠古原型#
劉徽註中的口訣是「以左行去右行(或相反),頭位相除,以下位損益」:用某一列去乘減另一列,把「頭位」(該未知數的係數)消掉。以第一題為例: 1. 以第三列 \(x+2y+3z=26\) 連乘 3 去減第一列 \(3x+2y+z=39\) 的頭位,得 \(-5y-8z=-39\) ; 2. 連乘 2 去減第二列的頭位,得 \(-y-5z=-18\) ; 3. 再以這兩條新的列互減消去 \(y\) 的頭位,得 \(4z=11\) ,故 \(z=11/4\) ; 4. 逐層回代,得 \(y=17/4\) 、 \(x=37/4\) 。
整個流程是:消元(化為上三角)→ 回代。這與兩千年後 Gauss 教科書中的程序完全同構。而「損益」二字——減不足者則「益」之——正是負數誕生的現場。
線索三:正負數運算法則——世界首創#
消元過程中會出現「小數減大數」的窘境,《九章》的解法是「正負術」:
正負術曰:同名相除,異名相益,正無入負之,負無入正之。
意即:同號相減、異號相加、(零減)正數得負數、(零減)負數得正數——這就是完整的減法與加法符號法則。西方要到歐洲文藝復興時期(Stifel、Cardano 前後)才勉強接受負數為「荒謬之數」(numeri absurdi)。中國算學家為了解聯立方程,被迫提前發明並坦然使用了負數。
線索四:與 Gaussian elimination 對照#
將《九章》的消去法翻譯成現代演算法:對增廣矩陣 \([A|b]\) 反覆執行
(對應古人「互乘相減」),把矩陣化為上三角後回代。差別只在:《九章》未做「除法標準化」(即消元時不把主元化為 1,用整數倍互乘避免分數),Gauss 則允許分數運算。古人為了算籌操作的方便,甚至發展出了「通分」「約分」的配套技術。 計算量上,兩者也各有取捨:《九章》的整數互乘使中間數字增長極快(算籌要重排擴位),Gauss 的分數消去法則保持數字小巧但引入捨入誤差——這個「整數精確 vs 分數高效」的取捨,正是今日計算機代數系統(符號計算用整數消去)與數值計算(浮點消去)分道揚鑣的遠古源頭。
線索五:與現代矩陣理論的深層對照#
把《九章》的方陣、消去法、回代翻成現代語言,會發現一整套概念的遠古預告: - 增廣矩陣 \([A|b]\) :籌算方陣本身就是它; - 初等列變換:互乘相減正是第三種初等列運算(某列的倍數從另一列減去),而且古人操作的是「整數倍互乘、最後一次相減」,天然避免中間出現分數——這在今日的整數消去法(fraction-free elimination,如 Bareiss 演算法)中有直接的現代後代; - 主元(pivot):「頭位」就是主元;消去時古人特別選「頭位最大」的列操作,隱含了選主元的穩定性直覺; - 秩與退化:方程章也有「無解」「解不唯一」的題目(虧損或矛盾的情形),古人知道某些方陣消到最後會出現「 \(0 = 非零\) 」的矛盾列——這正是秩虧偵測。
更令人驚訝的是:劉徽在註中還討論了「方程新術」——用比率(相當於今日的比值關係)重新改寫方程組,這是對解空間結構的早期思考。一個沒有符號代數的文明,居然摸到了矩陣消元、整數演算法、秩虧偵測三塊現代基石的邊緣。
程式碼範例:九章算術第一題的消去法 numpy 實作#
import numpy as np
from fractions import Fraction as F
# 九章算術·方程章第一題的增廣矩陣(用精確分數)
A = np.array([[F(3), F(2), F(1), F(39)],
[F(2), F(3), F(1), F(34)],
[F(1), F(2), F(3), F(26)]])
M = A.copy()
n = 3
for k in range(n): # 逐列消去「頭位」
for i in range(k + 1, n):
m = M[i, k] / M[k, k]
M[i, :] = M[i, :] - m * M[k, :]
x = np.zeros(n, dtype=object) # 回代
for i in range(n - 1, -1, -1):
s = M[i, n] - sum(M[i, j] * x[j] for j in range(i + 1, n))
x[i] = s / M[i, i]
print("上禾 =", x[0], "中禾 =", x[1], "下禾 =", x[2]) # 37/4, 17/4, 11/4
print("numpy 驗證:", np.linalg.solve(A[:, :3].astype(float),
A[:, 3].astype(float)))
兩條路徑——兩千年前的「損益」與今日的 linalg.solve——給出完全相同的答案:上禾 \(37/4\) 斗、中禾 \(17/4\) 斗、下禾 \(11/4\) 斗。證據鏈閉合。
結案 -- 後果與影響#
- 「方程」章確立了線性聯立方程組求解思想的源頭:把條件排成方陣、以消去法化簡、逐層回代——這正是矩陣消元與 Gaussian elimination 的原型,比 Gauss 早近兩千年。
- 正負數運算法則作為解方程的工具被系統化,是人類數學史上最早對負數運算的明文規定,並由劉徽在註中進一步闡明。負數從「實務需要」誕生,比西方坦然接受早了一千多年。
- 整數消去法的遠古原型:互乘相減避免分數的策略,在今日的 fraction-free elimination(Bareiss 演算法,用於計算機代數系統)中有直接的現代後代。
- 劉徽 263 年注《九章》,其「方程」註文成為後世(如宋元時期秦九韶、朱世傑的「天元術」「四元術」)發展高次與多元方程的基礎;朱世傑 1303 年的《四元玉鑒》甚至處理了四元高次聯立方程——消去法路線在東亞延續了千年。
- 遺憾的是,這條技術路線在明清之際逐漸失傳;西方世界對消去法與行列式的重新發明要等到 Leibniz(1693)、Cramer(1750)、Gauss(1810 年前後)的時代。今天教科書裡的 Gaussian elimination,其實有一個早了兩千年的東方前身——這是數學史上「發明—失傳—再發明」最長的一條懸案。
關鍵人物與文獻#
| 人物 | 角色 |
|---|---|
| 《九章算術》無名作者群 | 記載「方程」章的算學家們 |
| 劉徽 | 公元 263 年注《九章》,留下消去法與正負術的完整證詞 |
- 《九章算術》卷八「方程」,成書約公元前一世紀(內容更早)。
- 劉徽《九章算術注》(263 年)。
- 郭書春《九章算術譯注》;李儼、杜石然《中國古代數學簡史》。