陳凱 上海市位育中學
《周易·系辭》說:“天一、地二、天三、地四、天五、地六、天七、地八、天九、地十;天數五,地數五,五位相得而各有合。天數二十有五,地數三十,凡天地之數五十有五。”周易所說的這種取自然數中前5個奇數和前5個偶數相加和為55的情形,大概是古人覺得55這個數字有著非同一般的寓意。實際上,其他數列的運算中也常能見到55,如連續取5個相連的三角形數3、6、10、15、21的和是55,1、2、3、4、5的平方數之和也是55,斐波那契數列的第10項也是55。這些運算或許可以給算法教學帶來些許趣味。
有學者認為要從計算工具使用的角度,來揣摩古人對于數的理解。[1]例如,簡單地擺放2根算籌,自然而然就會在中間形成1個空位,這就是天一地二說法的由來。筆者由此想到,古人在使用算籌來計數時,表示每一位數字最多用5根算籌,為了表示6,就需要用一根方向不同的算籌代表5根算籌。圖1所示就是一種縱式的算籌擺放樣式,當數字超過5時,就用一根橫放的算籌代表5。如果用10根算籌擺放成55,那么無論是十位數加1,還是個位數加1,都會使算籌的擺放形態發生重大的變化,這或許是55的另一個特殊之處吧。

圖1 縱式算籌擺放樣式
一般人大概不太會注意到,算籌加1運算有其奇特之處,在5向6變化過程中,對于縱向擺放的算籌,事實上并非滿5發生變化,而是滿6才發生變化,當縱向算籌滿6時,需要將縱向的5根算籌換為1根橫向算籌,同時留下1根縱向的算籌。而在9向10變化的過程中,對于所有算籌,也可以看成是滿6發生變化,當在4根縱向算籌和1根橫向算籌的基礎上再加上1根算籌,就需要將所有縱向算籌和1根橫向算籌全部清空并進位。橫式算籌擺放方法如圖2所示,如果將數字5用擺成橫向的1根算籌來表示,就會和橫式算籌擺放樣式中數字1所使用的圖樣發生混淆。可見,算籌樣式的成型,與編碼的唯一性有關。

圖2 橫式算籌擺放樣式
用算籌做加法,一般采用兩種方法,其一是逐個增加算籌并實施算籌滿6時的變化,其二是采用算籌歌訣做快速的變化。筆者未查閱到完整的算籌歌訣,但因為珠算實際就是從算籌演化而來,因此,珠算口訣直接就可以用于算籌,如“六上一去五進一”的口訣,對算籌的使用,就是指當已有數字存在上籌(縱式中的橫杠或橫式中的豎杠,借用珠算中上珠的稱謂,暫且稱作上籌)的時候,為了加上數字6,就需要移除上籌,再添加上1根算籌,同時進位添加1根算籌。很顯然,這就是一種算法。這種算法很適合于人來使用,而且,根據算籌的計算原理發明出算盤之后,采用這樣的算法會使得計算速度有極大的提高。但對于某個完全自動化運轉的裝置來說,采用這樣的算法卻是徒增麻煩。可見,哪一種算法更適用,與可用和被使用計算之物有關。
將算籌的變化和用計算機模擬算籌的變化這兩件事聯系在一起,就能發現,實現“如何變”的方法是極其豐富的。算法之變和人們可借助的計算之物有著密切的關聯,這里的“物”指的既可能是物質實體,也可能是一種虛擬物,
例如,可以利用if語句編寫簡單的程序,將數字轉化為算籌的擺放樣式,為簡便起見,這里暫且只考慮輸入一位數的情況。方法一如圖3所示,程序中用“|”代表1根算籌,用“____”代表1根上籌,程序中“ ”的作用是回車換行,目的是用上下兩行符號拼接成一個類似算籌擺放的圖樣。為后續行文方便將此方法稱為方法一,這種方法體現出一種映射關系,即對于輸入的數字符號,一定有其對應的輸出的算籌擺放樣式。

圖3 一種將數字轉化為算籌擺放樣式的程序代碼
如果將變量x視作某物,將分支結構的語句也視作某物,若在頭腦中將語句與變量之間的協同變化比擬成某實體裝置的變化,則想象出來的場景很可能是會有很大差異的。例如,可以想象變量x是一個帶有某種屬性的不動的物體,而分支結構的機械裝置則會獲取其屬性狀態并給出相對應的輸出;或者,變量x是一個運動著的物體,由某個類似流水線的裝置將其載入到某個判別裝置中……這樣的想象能讓人發現頭腦對于虛擬之物的利用是超越物質實體的,在想象過程中,判別裝置的行為好像一個黑箱,其內部究竟是如何工作的,則是更難想象出來的事情。
另一種實現方法是將算籌所有的擺放樣式都存放于列表中,然后將輸入的數字作為索引號,從列表中調取出擺放樣式。這里將其稱為方法二(如圖4)。

圖4 從列表調取算籌擺放樣式
這些存儲在列表中的圖樣很容易存儲到其他存儲器中,這樣就容易實現數據的共享,在一定程度上體現出存儲數據的意義。類似的方法有時會在計算機的算力有限時使用,如一些單片機在進行比較復雜的函數計算時,為了加快計算時間,就會采用數學計算和查表結合的方法來得到函數計算結果。[2]
可以想象某個機械裝置根據用戶輸入的特定序號,到存儲空間中選取出相應的圖樣。想象中的工作場景顯然和方法一是有很大區別的。
不妨將先前的程序代碼與圖5所示的方法三的程序代碼做一個比較。由于算籌圖樣在增加的過程中有一定的規律,所以,可以通過數字的比較來判斷是否滿6,由此確定是否要添加1根上籌,而其他縱向擺放的算籌都可以自動化批量生成。雖然說這三種方法都自動化地實現了數字轉換為算籌擺放樣式的工作,但方法三這個自動化裝置的內部構造本身存在著某種自動化,方法一和方法二的自動化是針對使用者來說的,而方法三的自動化是針對裝置的制造者來說的,如果方法三的自動化裝置是一個實體裝置,其設計和制造的難度顯然是要高于前者的,相對前者,它還要實現構造上的自動化,具有一定程度的元自動化的特征。

圖5 使用分支結構實現數字轉化為算籌擺放樣式的程序代碼
用數字乘上字符串是Python中特有的方法,如果使用其他程序語言,就需要采用循環結構的語句來批量按數字生成縱向的算籌圖樣。如果將Python語言視作虛擬物,那么這種物具有其他程序語言所不具備的特征,王榮良老師提出,算法的學習需要體現計算模型的構造過程,學生需要用具體編程語言來表達算法,這不是單純用流程圖能替代的。[3]筆者從計算之物能力切入進行思考,也能得到同樣的結論。
這里有一個有趣的問題,如果是由人來擺放算籌,那么此人頭腦中的過程,是更接近于方法一、方法二還是方法三呢?設想某人初學算籌,在開始階段,大概是必須要記得滿6就將5根算籌換成1根上籌,這就需要簡單的邏輯判別,比較接近于方法三的程序代碼中分支結構判斷的行為,如果此人使用算籌已經非常熟練,那么估計頭腦中就會建立起牢固的數字和算籌擺放樣式的對應關系,則更接近方法一和方法二代碼的行為方式。
甚至于可以單純使用數學模型來替代分支結構的邏輯判斷,程序代碼如圖6所示。

圖6 采用數學模型實現數字轉化為算籌擺放樣式的程序代碼
這種方法距離人的頭腦的行為方式就非常遠了,如果不是有意要利用數學,人們的頭腦幾乎很少用這樣的方式來解決日常性的問題。假設有某個實施判別動作的機械裝置,如果這個機械裝置內部是借助數學運算來進行決策的,那么人的頭腦是很難想象出這種數學運算是如何與裝置內部物質實體的變化相關聯的。這就說明,計算機程序中的數學公式已然成為一種可用于計算的虛擬物,它和數學課堂里的數學公式的意義是有所不同的。同時,計算機之所以具有當前的計算能力,和底層的數學模型是密切相關的,人們之所以很難發現這些底層數學模型是如何工作的,是因為人們所接觸的系統,是由底層模型一層層虛擬出來的某種計算模型。
某些計算模型對實現算籌擺放樣式生成相當有用。例如,可以借助字符串重寫模型來實現將數字轉換為算籌擺放樣式的功能,程序代碼如圖7所示。這種方法相當于是將數字所對應的算籌全部擺放到桌面上,然后將其中6根算籌替換成1根算籌和1根代表著5根算籌的上籌。這種方法顯然很不符合人的思維習慣,但對于一個具有替換功能的裝置來說——如果的確有的話,實現起來卻相當簡單。但這樣具有替換功能的裝置卻是不太容易制造出來的,雖然Python中有replace函數可用,但本質上,是因為首先有了Python這種圖靈完備的計算裝置,然后才在此基礎上編寫出replace函數。所以說,雖然字符串重寫模型很容易實現算籌樣式變化,但這個模型本身卻是不容易構造出來的。而其他多種計算模型,如元胞自動機、Tag系統等,在實現算籌擺放樣式的模擬上都比較困難,而如圖靈機、時序數字電路這樣的計算模型在實現時就相對容易一些。有意思的是,人們可以用一種計算模型來模擬另一種計算模型,如用元胞自動機來模擬圖靈機,然后用圖靈機來模擬算籌擺放樣式。

圖7 用字符串替換的方法實現數字轉化為算籌擺放樣式的程序代碼
Python中的replace函數將替換功能內部的復雜性掩蓋了起來,替換的實現并非自然而然的。設想一下,如果現實世界中存在某種神奇的自然之物,可以實現符號編碼的匹配和替換,那么人們或許就會直接利用這種東西來實施自動化的計算,并且演化出各種基于匹配和替換的算法了。對計算的實現而言,人思維中的自然的變化和現實世界中自然的變化是很不相同的,人需要利用自然界中的自然之物的變化,去構造出一種能夠模仿人的思維變化的東西。莊子說,“圣人者,原天地之美而達萬物之理,是故至人無為,大圣不作,觀于天地之謂也”,前半句很有道理,后半句卻值得商榷,假設人腦所構造的計算模型無法由實際的計算裝置開展直觀地運算,那么人在通達萬物之理的路途上就會遇到很大的阻礙。