建構式數學

公元 1999 年,教育部推動建構式數學。我躬逢其盛,恰好踏入小學。同胞們印象深刻也最為詬病的莫過於乘法。

在改革以前,學生必須背誦九九表。九九表存在了約 3000 年,配合十進制與算籌,為東亞算學的基礎。

改革後,九九表成為校園內的禁忌。當學生面對

7 × 9

若在作業本寫下

7 × 9 = 63

只會換來一個

學生必須寫下

7 × 1 = 7
7 × 2 = 14
7 × 3 = 21
7 × 4 = 28
7 × 5 = 35
7 × 6 = 42
7 × 7 = 49
7 × 8 = 56
7 × 9 = 63

這是一場災難。對數學和程式來說,可靠和速度都很重要,不可偏廢;就好像談戀愛,堅貞和吸引力都是關鍵。

建構式數學真的很糟糕嗎?

不,建構式數學非常可靠。它在台灣造成問題,是因為數學的速度被刻意忽略了。數學的速度也是很重要的,加、解密的時間複雜度不同,是 RSA 的賣點。

此外,給 7 × 9 = 63 一個大叉不合理。

算術的建構

以現代數學而言,建構式數學還幹得不夠徹底。所有的數學實體都是集合,序數則是在集合論上玩弄算術。然而這樣很容易嚇跑小學生,所以我們可以從看起來比較像是人話的皮亞諾公理出發。

  1. 0 是自然數。
  2. 每個自然數 A 都有一個後繼 suc A,而 suc A 也是個自然數。
  3. 0 不會是某個自然數的後繼。
  4. 如果 suc A = suc B ,則 A = B
  5. 任何關於自然數的敘述,如果對 0 而言成立,而且只要對 n 成立就對 suc n 成立,那麼這個敘述對所有的自然數都成立。
以上五條聯結是 Metamath 中嚴密的集合論推理。最後一條確立了數學歸納法,建議推遲到中學再處理。

遞迴

現代數學已經有超限遞迴了,所以 Metamath 中的遞迴的定義才會這麼長。在此我們只討論自然數的情形,此時 Metamath 中的遞迴的行為就如同以下這支 JavaScript 程式。

function rec(F, I)
{
	return function(A)
	{
		for (var k = 0; k < A; ++k)
			I = F(I);
		return I;
	}
}

如果這樣看了還是沒感覺,那就只好舉例了。一例勝千言?

(rec({⟨x, y⟩ ∣ y = suc x}, 1) ‘2) = suc suc 1 = 3(1)

其中 {⟨x, y⟩ ∣ y = suc x} 的樣子有點可怕,它只是個函數,滿足 {⟨x, y⟩ ∣ y = suc x} ‘A = suc A

加法的建構

直接定義 + 為一個集合是比較到位的集合論作法,但可能會嚇到觀眾。所以請容我偷賴,直接定義加法的結果

(A + B) = (rec({⟨x, y⟩ ∣ y = suc x}, A) ‘B)

也就是以 A 為啟始值,執行 B 次 suc 的結果。如此 (1) 式就可以簡化為

(1 + 2) = 3

乘法的建構

我們如法炮製。

(A · B) = (rec({⟨x, y⟩ ∣ y = (x + A)}, 0) ‘B))

也就是以 0 為啟始值,加了 BA 的結果。所以 (2 · 3) 會展開為 (((0 + 2) + 2) + 2)

給 7 × 9 = 63 一個大叉不合理

因為你必須也給 7 + 9 = 16 一個大叉

我的教學態度

在批評之後,作為負責任的反對黨,我要提出我心目中的教學方法。

查表是正確的方法

在西方有了天文、地理的大發現之後,因應曆算、製圖等需求,要有大量的乘除、開方等運算。為了降低計算的時間複雜度,數學家先算出對數表供人查詢。直到 FFT 的發現與計算機的普及才逐漸式微。

如果是要算數,而不是證明,查表能得到答案,而且不可恥。

要教學生背誦以外最快的算法

除了背誦九九表之外,我們應該教學生背誦以外最快的算法——古埃及乘法

7 × 1 = 7
7 × 2 = 14
7 × 4 = 28
7 × 8 = 56

7 × 9 = 63