陳凱 上海市位育中學
用計算機程序對兩個變量的數值進行比較是非常容易的事,但若要對三個變量的數值進行比較,雖然只是增加了一個變量,但是代碼卻要復雜很多,代碼的編寫方法也變得五花八門,這不免讓人對三這個數字投去特別的目光。有研究復雜系統的學者指出,兩個對象只能體現對比,而三個對象能產生交織,周期三蘊含著混沌。[1]
中文里有許多帶數字三的詞匯,有時候這個三表示多,如三番五次、三令五申、三姑六婆,有時候也表示少,如三言兩語、三杯兩盞,有時候還用來表示頻繁,如接二連三、三天兩頭……三這個數字處在狀態發生變化的交界線上,老子說,道生一,一生二,二生三,三生萬物,那么為何是三生萬物。有學者揣摩老子的想法,認為三這個數字能體現出陰陽二元對立中的激蕩變化,是造成復雜的氣化物生過程的初始值。[2]筆者認為,在對變量數值大小進行比較的任務中,三個變量數值相互比較的計算過程初步體現出復雜性,其中蘊藏的某些可識別的模式可能成為形式化地構造更為復雜的計算過程的開端,也可以作為三生萬物在計算思維范疇上的解釋。
要編寫比較兩個變量中數值大小的程序(假設變量值不相等),是相當容易的事情,Python代碼片段如圖1所示。

圖1 打印兩個變量中存有較大數值的變量名
對初學者來說,若是用如圖2所示的嵌套分支結構的語句,來對三個變量中的數值進行比較,確認哪個變量中的數值最大(假設變量值不相等),很大的難點在于邏輯的推斷過程。例如,應該先比較哪兩個變量?比較完成后對不同的兩個結果,又應該再比較哪兩個變量?

圖2 使用嵌套分支結構打印三個變量中存有最大數值的變量名
可以將變量想象成三個形狀一樣的盒子,而盒子中裝有數量不等的球。當盒子與盒子碰撞時,裝有更多球的盒子會閃光。那么,借助盒子閃光的狀況,最多碰撞兩次,就可以確定哪個盒子中球的數量最多了。設三個盒子為A、B、C,推理過程如下:將A盒與B盒碰撞,如A盒閃光,則將A盒與C盒碰撞,如A閃光則A盒中球最多,否則C盒中球最多;如果A盒與B盒碰撞后B盒閃光,則將B盒與C盒碰撞,如B閃光則B盒中球最多,否則C盒中球最多。這樣的推理過程可以平滑地轉換成高級語言中嵌套分支的代碼。如果將上述三個盒子之間的比較換作三個人之間進行比武的場面,則更容易理解其中的判斷過程。顯然,相對于抽象的判斷過程而言,一般人的頭腦更擅長于具象的判斷過程。
不過,如果是四個盒子或五個盒子,甚至更多的盒子,比較過程是怎樣的呢?為了更清晰地看出比較過程的規律,可以將代碼改成“if-elif”的結構,如圖3所示。

圖3 讓判斷條件更清晰的嵌套分支結構代碼
將程序代碼中進行比較的變量名單獨列出,不管其比較含義,只看變量名的順序和位置,就能發現其中存在某種模式:兩個變量名并列,以及兩個變量名前后交換并列,置放于左側另起的第一層,然后取出并列的變量名中的首個變量名,再和第三個變量名并列以及交換,然后重復剛才的動作(如圖4)。

圖4 分支結構中的變量名變化模式
很顯然,如果只有兩個變量,是不可能歸納出任何變化模式的。然而,一旦領悟到三個變量的比較模式,對于更多變量,就可以一直套用這個模式進行比較,就仿佛是頭腦中的三生萬物。圖5是對于四個變量找出存有最大數變量的模式和對應的程序代碼。有趣的是,只需套用模式就能編寫出正確的程序代碼,而完全不用管其中涉及的邏輯原因。通過此模式,程序代碼可以被自動構造出來,并經由測試被證明是可行的。

圖5 使用嵌套分支結構打印四個變量中存有最大數值的變量名的模式和程序代碼
想象有一個控制系統控制著機械手臂并通過盒子的碰撞來找出裝有最多球的盒子,假設從a、b、c開始編號的許多盒子已經就位,而這個裝置用機械手臂取出各個盒子進行比較,然后又將這些盒子放回原處,直到根據分支結構的流程找到了裝有最多球的盒子。如果需要比較的盒子數量增加,那么需要改變的是控制系統的程序,而不需要改變盒子。
仔細揣摩上述嵌套分支結構中變量比較的模式,或者干脆將多個盒子之間的比較的場景換成多人比武的場景,發現存在這樣的模式,是對應有現實上的原因的,上述變量比較模式變化的含義,實質上是將大小比較中“失敗”的變量拋棄掉,而用“獲勝”的變量與下一個變量進行比較。值得一提的是,筆者自己并不是直接領悟了此原因,而是從模式變化中猜想到了這個原因并通過運行代碼驗證了其可行性。
從這個本質原因出發,可以重新構造其他類型的比較模式,想象有一系列代表變量的盒子,每兩個盒子比較后,將“失敗”的盒子拋棄,這個過程可以用列表形象地展現出來,如圖6所示。只有在存在三個盒子或更多盒子的情況下,這種“比較—拋棄”的模式才能被清晰地顯現出來。

圖6 比較兩次并拋棄兩次“失敗者”
三個變量需要執行兩次分支結構的判斷,顯然,可以推理知道n個變量需要n-1次分支結構的判斷。因此,代碼可以改成循環結構的形式,如圖7所示。

圖7 用循環結構比較并拋棄“失敗者”
根據以上代碼可以想象出如下圖景:盒子被整齊地放成一排,有兩只機械手臂抓起相鄰盒子判斷盒子內球的多少,并根據判斷結果做出拋棄某一個盒子的動作,在這個過程中,只要剩下的盒子自動靠攏排整齊,那么機械手臂的行為方式就始終是一致的,控制機械手臂的控制系統無需因為盒子數量的增加而更換代碼。不過,和上一小節比較過程形成鮮明區別的是,比較過程中盒子的數量會發生變化。
也可以設想另一種圖景:存在一種可以“分身”的控制系統,它用一只機械手臂抓著一個盒子,而另一只機械手臂抓取的盒子是由這個控制系統的“分身”選取出的比較勝利者。這個圖景在現實中顯然是不可能存在的,但這個虛構的控制系統卻能對應真正的計算過程,按此種圖景可以編寫出利用函數的模塊化比較三個變量數值大小的程序代碼,如圖8所示。

圖8 一種利用函數模塊化比較三個變量數值大小的程序代碼
回到嵌套分支結構尋找裝有最多球盒子的問題,當盒子數量發生變化后,控制中心就需要修改控制的方式,不過,因為比較方式的變化本身也存在規律,所以就存在一種可能,可以另外設計一個能夠修改此控制系統控制方式的上一層次的控制系統。但怎么實現呢?考慮到控制指令本身也可以是一種數據,那么只要對數據進行有規律的迭代,就能得到有規律的控制指令。圖9所示的是一種比較指令生成的片段與運行結果,其中用數字1代表變量a,數字2代表變量b,以此類推,可以將此運行結果和圖5中的變量比較變換模式作對比。雖然只是片段,但已經證明了用程序代碼生成程序代碼的可能性。程序代碼中對存放比較指令的數據做了兩次迭代,顯然,迭代代碼本身也存在著規律的變化模式,還可以進一步用循環結構使迭代過程更加自動化。

圖9 按變化模式生成比較指令的程序代碼
勉強用語言來描述這個多層次的控制系統的運行方式:一個內部控制系統根據已有的存儲區域中盒子里的球的數量,有規則地按替換模式更換盒子。而所更換的盒子里的球的數量也就是數據,則成為外部控制系統進行比較時抓取哪一個盒子的依據。
本文重點不在編程語言,也不在算法,而是算法在某些特定環境條件約束下的變化模式,圖景想象的方法是一種有用的工具,它將人從用算法解決實際問題的思維框架中拉出,借助復雜性的思維來思考算法本身可能存在的變化模式。