陳凱 上海市位育中學(xué)
用計算機(jī)程序?qū)蓚€變量的數(shù)值進(jìn)行比較是非常容易的事,但若要對三個變量的數(shù)值進(jìn)行比較,雖然只是增加了一個變量,但是代碼卻要復(fù)雜很多,代碼的編寫方法也變得五花八門,這不免讓人對三這個數(shù)字投去特別的目光。有研究復(fù)雜系統(tǒng)的學(xué)者指出,兩個對象只能體現(xiàn)對比,而三個對象能產(chǎn)生交織,周期三蘊(yùn)含著混沌。[1]
中文里有許多帶數(shù)字三的詞匯,有時候這個三表示多,如三番五次、三令五申、三姑六婆,有時候也表示少,如三言兩語、三杯兩盞,有時候還用來表示頻繁,如接二連三、三天兩頭……三這個數(shù)字處在狀態(tài)發(fā)生變化的交界線上,老子說,道生一,一生二,二生三,三生萬物,那么為何是三生萬物。有學(xué)者揣摩老子的想法,認(rèn)為三這個數(shù)字能體現(xiàn)出陰陽二元對立中的激蕩變化,是造成復(fù)雜的氣化物生過程的初始值。[2]筆者認(rèn)為,在對變量數(shù)值大小進(jìn)行比較的任務(wù)中,三個變量數(shù)值相互比較的計算過程初步體現(xiàn)出復(fù)雜性,其中蘊(yùn)藏的某些可識別的模式可能成為形式化地構(gòu)造更為復(fù)雜的計算過程的開端,也可以作為三生萬物在計算思維范疇上的解釋。
要編寫比較兩個變量中數(shù)值大小的程序(假設(shè)變量值不相等),是相當(dāng)容易的事情,Python代碼片段如圖1所示。
圖1 打印兩個變量中存有較大數(shù)值的變量名
對初學(xué)者來說,若是用如圖2所示的嵌套分支結(jié)構(gòu)的語句,來對三個變量中的數(shù)值進(jìn)行比較,確認(rèn)哪個變量中的數(shù)值最大(假設(shè)變量值不相等),很大的難點(diǎn)在于邏輯的推斷過程。例如,應(yīng)該先比較哪兩個變量?比較完成后對不同的兩個結(jié)果,又應(yīng)該再比較哪兩個變量?
圖2 使用嵌套分支結(jié)構(gòu)打印三個變量中存有最大數(shù)值的變量名
可以將變量想象成三個形狀一樣的盒子,而盒子中裝有數(shù)量不等的球。當(dāng)盒子與盒子碰撞時,裝有更多球的盒子會閃光。那么,借助盒子閃光的狀況,最多碰撞兩次,就可以確定哪個盒子中球的數(shù)量最多了。設(shè)三個盒子為A、B、C,推理過程如下:將A盒與B盒碰撞,如A盒閃光,則將A盒與C盒碰撞,如A閃光則A盒中球最多,否則C盒中球最多;如果A盒與B盒碰撞后B盒閃光,則將B盒與C盒碰撞,如B閃光則B盒中球最多,否則C盒中球最多。這樣的推理過程可以平滑地轉(zhuǎn)換成高級語言中嵌套分支的代碼。如果將上述三個盒子之間的比較換作三個人之間進(jìn)行比武的場面,則更容易理解其中的判斷過程。顯然,相對于抽象的判斷過程而言,一般人的頭腦更擅長于具象的判斷過程。
不過,如果是四個盒子或五個盒子,甚至更多的盒子,比較過程是怎樣的呢?為了更清晰地看出比較過程的規(guī)律,可以將代碼改成“if-elif”的結(jié)構(gòu),如圖3所示。
圖3 讓判斷條件更清晰的嵌套分支結(jié)構(gòu)代碼
將程序代碼中進(jìn)行比較的變量名單獨(dú)列出,不管其比較含義,只看變量名的順序和位置,就能發(fā)現(xiàn)其中存在某種模式:兩個變量名并列,以及兩個變量名前后交換并列,置放于左側(cè)另起的第一層,然后取出并列的變量名中的首個變量名,再和第三個變量名并列以及交換,然后重復(fù)剛才的動作(如圖4)。
圖4 分支結(jié)構(gòu)中的變量名變化模式
很顯然,如果只有兩個變量,是不可能歸納出任何變化模式的。然而,一旦領(lǐng)悟到三個變量的比較模式,對于更多變量,就可以一直套用這個模式進(jìn)行比較,就仿佛是頭腦中的三生萬物。圖5是對于四個變量找出存有最大數(shù)變量的模式和對應(yīng)的程序代碼。有趣的是,只需套用模式就能編寫出正確的程序代碼,而完全不用管其中涉及的邏輯原因。通過此模式,程序代碼可以被自動構(gòu)造出來,并經(jīng)由測試被證明是可行的。
圖5 使用嵌套分支結(jié)構(gòu)打印四個變量中存有最大數(shù)值的變量名的模式和程序代碼
想象有一個控制系統(tǒng)控制著機(jī)械手臂并通過盒子的碰撞來找出裝有最多球的盒子,假設(shè)從a、b、c開始編號的許多盒子已經(jīng)就位,而這個裝置用機(jī)械手臂取出各個盒子進(jìn)行比較,然后又將這些盒子放回原處,直到根據(jù)分支結(jié)構(gòu)的流程找到了裝有最多球的盒子。如果需要比較的盒子數(shù)量增加,那么需要改變的是控制系統(tǒng)的程序,而不需要改變盒子。
仔細(xì)揣摩上述嵌套分支結(jié)構(gòu)中變量比較的模式,或者干脆將多個盒子之間的比較的場景換成多人比武的場景,發(fā)現(xiàn)存在這樣的模式,是對應(yīng)有現(xiàn)實(shí)上的原因的,上述變量比較模式變化的含義,實(shí)質(zhì)上是將大小比較中“失敗”的變量拋棄掉,而用“獲勝”的變量與下一個變量進(jìn)行比較。值得一提的是,筆者自己并不是直接領(lǐng)悟了此原因,而是從模式變化中猜想到了這個原因并通過運(yùn)行代碼驗(yàn)證了其可行性。
從這個本質(zhì)原因出發(fā),可以重新構(gòu)造其他類型的比較模式,想象有一系列代表變量的盒子,每兩個盒子比較后,將“失敗”的盒子拋棄,這個過程可以用列表形象地展現(xiàn)出來,如圖6所示。只有在存在三個盒子或更多盒子的情況下,這種“比較—拋棄”的模式才能被清晰地顯現(xiàn)出來。
圖6 比較兩次并拋棄兩次“失敗者”
三個變量需要執(zhí)行兩次分支結(jié)構(gòu)的判斷,顯然,可以推理知道n個變量需要n-1次分支結(jié)構(gòu)的判斷。因此,代碼可以改成循環(huán)結(jié)構(gòu)的形式,如圖7所示。
圖7 用循環(huán)結(jié)構(gòu)比較并拋棄“失敗者”
根據(jù)以上代碼可以想象出如下圖景:盒子被整齊地放成一排,有兩只機(jī)械手臂抓起相鄰盒子判斷盒子內(nèi)球的多少,并根據(jù)判斷結(jié)果做出拋棄某一個盒子的動作,在這個過程中,只要剩下的盒子自動靠攏排整齊,那么機(jī)械手臂的行為方式就始終是一致的,控制機(jī)械手臂的控制系統(tǒng)無需因?yàn)楹凶訑?shù)量的增加而更換代碼。不過,和上一小節(jié)比較過程形成鮮明區(qū)別的是,比較過程中盒子的數(shù)量會發(fā)生變化。
也可以設(shè)想另一種圖景:存在一種可以“分身”的控制系統(tǒng),它用一只機(jī)械手臂抓著一個盒子,而另一只機(jī)械手臂抓取的盒子是由這個控制系統(tǒng)的“分身”選取出的比較勝利者。這個圖景在現(xiàn)實(shí)中顯然是不可能存在的,但這個虛構(gòu)的控制系統(tǒng)卻能對應(yīng)真正的計算過程,按此種圖景可以編寫出利用函數(shù)的模塊化比較三個變量數(shù)值大小的程序代碼,如圖8所示。
圖8 一種利用函數(shù)模塊化比較三個變量數(shù)值大小的程序代碼
回到嵌套分支結(jié)構(gòu)尋找裝有最多球盒子的問題,當(dāng)盒子數(shù)量發(fā)生變化后,控制中心就需要修改控制的方式,不過,因?yàn)楸容^方式的變化本身也存在規(guī)律,所以就存在一種可能,可以另外設(shè)計一個能夠修改此控制系統(tǒng)控制方式的上一層次的控制系統(tǒng)。但怎么實(shí)現(xiàn)呢?考慮到控制指令本身也可以是一種數(shù)據(jù),那么只要對數(shù)據(jù)進(jìn)行有規(guī)律的迭代,就能得到有規(guī)律的控制指令。圖9所示的是一種比較指令生成的片段與運(yùn)行結(jié)果,其中用數(shù)字1代表變量a,數(shù)字2代表變量b,以此類推,可以將此運(yùn)行結(jié)果和圖5中的變量比較變換模式作對比。雖然只是片段,但已經(jīng)證明了用程序代碼生成程序代碼的可能性。程序代碼中對存放比較指令的數(shù)據(jù)做了兩次迭代,顯然,迭代代碼本身也存在著規(guī)律的變化模式,還可以進(jìn)一步用循環(huán)結(jié)構(gòu)使迭代過程更加自動化。
圖9 按變化模式生成比較指令的程序代碼
勉強(qiáng)用語言來描述這個多層次的控制系統(tǒng)的運(yùn)行方式:一個內(nèi)部控制系統(tǒng)根據(jù)已有的存儲區(qū)域中盒子里的球的數(shù)量,有規(guī)則地按替換模式更換盒子。而所更換的盒子里的球的數(shù)量也就是數(shù)據(jù),則成為外部控制系統(tǒng)進(jìn)行比較時抓取哪一個盒子的依據(jù)。
本文重點(diǎn)不在編程語言,也不在算法,而是算法在某些特定環(huán)境條件約束下的變化模式,圖景想象的方法是一種有用的工具,它將人從用算法解決實(shí)際問題的思維框架中拉出,借助復(fù)雜性的思維來思考算法本身可能存在的變化模式。