步驟e3中的箭頭←是最重要的替代運算(有時叫做賦值或代換)。「m←n」意思是變數m的值代之以變數n的當前值。當演算法e開始時,m和n的值是原來給出的數;但當它結束時,一般說來,這些變數將有不同的值。箭號用來把替代運算同相等關係加以區別:我們將不說「置m=n」,但也許我們將要問「是否m=n?」「=」號標記乙個可被測試的條件,「←」號標記乙個可被執行的動作。n增1的運算通過「n←n+1」來標記(讀作以n+1代替n)。一般說來,「變數←式子」意味著用出現於式子內的所有變數的當前值來計算式子,並將結果代替箭頭左邊的變數的原先值。不熟悉計算機工作的人們有時有這樣的傾向,即用「n→n+1」來標記n增1的運算,說是「n變成n+1」,這只能導致混亂,因為它同標準的約定正相衝突,因此我們應加以避免。
注意,步驟e3中的動作的次序是重要的。「置m←n,n←r」完全不同於「置n←r,m←n」。因為後者意味著,在用n的值來置m之前,n的原先值已經失去了。因此,後一次序等價於「置n←r,m←r」。當若干個變數全都置成等於相同的量時,我們使用多重的箭頭;於是,「n←r,m←r」可以寫成「n←m←r」。兩個變數的值相互交換,我們可以寫成「交換m〈—〉n」;這一動作也可通過使用乙個新變數t,並寫「置t←m,m←n,n←t」來確定。
除非另有說明,乙個演算法總是在編號最小的步驟處,通常是在步驟1處開始,並且順序逐步執行之。在步驟e3中,強制的「返回步驟e1」以一種明顯的方式說明了進行計算的次序。在步驟e2中,以條件「若r=0」作為動作進行的前提條件;所以,如果r≠0,就不應用該句子的剩下的話。雖然這裡並未說明動作,但我們總是理解成:可以加上不言而喻的句子「如果r≠0,進行步驟e3」。
粗豎線■,出現於步驟e3的末尾,用來表示演算法已結束而正文將繼續。
至此,我們實際上討論了用於本書的演算法中的所有記號的約定,剩下只需要來約定乙個用來表示乙個有序陣列的元素的「帶下標的」或「帶足標的」項的記號了。假設我們有n個量ν1,ν2,…,νn;對於第j個元素νj,我們經常使用記號ν〔j〕來代替。類似地,對於雙下標的記號如αij,有時更願意使用α〔i,j〕。有時,變數用多個字母的名稱,並把這些字母都寫成大寫的。例如,temp可以是乙個用於暫時地儲存乙個計算的值的變數的名稱,prime〔k〕可以表示第k個質數,等等。
關於演算法的形式就談這麼多;現在讓我們來實現乙個演算法看一看。應當立即指出,讀者不要期望象讀一篇**那樣來讀乙個演算法。如果是抱著這樣的企圖的話,那他就會很難理解文中正在敘述的內容。乙個演算法應該是可以信賴的,而且學習乙個演算法直到徹底掌握的最好方法是反覆進行試驗。當讀者遇到乙個演算法時,應立即拿起筆和紙,找出這個演算法的乙個例子;通常要給出這個例子的要點,要不然就是讀者能容易地把它想象出來。為了能夠理解乙個給定的演算法,這是乙個簡單且省力的方法,經驗證明,其它的途徑都是不成功的。
因此,讓我們找出演算法e的乙個例子。假設我們給出m=119和n=544。讓我們準備好由步驟e1開始(讀者現在就要象我們在下邊一步一步敘述的那樣來遵循這個演算法)。在這種情況下,以n除m是十分簡單的。簡直是太簡單了,因為商是0,而餘數是119。於是r←119。我們進行到步驟e2,而且由於r≠0,步驟e2不發生作用。在步驟e3,我們置m←544,n←119。顯然,如果原來m<n,步驟e1中的商數總為0,而演算法總是首先以這樣比較麻煩的方式來相互交換m和n。如果願意,我們可以增加乙個新的步驟:「e0〔保證m≥n〕如果m<n,交換m〈—〉n。」這樣一來,在演算法中並無實質性的改動,只是增加了演算法的長度,但卻大約有一半的情況減少了用來實現演算法所需的時間。
返回到步驟e1,我們發現544/119=4又68/119。所以r←68。步驟e2再一次不能用。而且在步驟e3,我們置m←119,n←68。下一輪置r←51,而且最終m←68,n←51。然後,r←17,且m←51,n←17。最後,當以17除51時,r←0,於是演算法在步驟e2處結束。119和544的最大公因子是17。
所以乙個演算法就是這樣。演算法的現代的意義十分類似於處方、過程、方法、技術、規程、程式,只不過是演算法一詞的含意有時稍微有一點特殊。乙個演算法,就是乙個有窮規則的集合,其中之規則規定了乙個解決某一特定型別的問題的運算序列;此外,乙個演算法還有五個重要的特性:
1)有窮性 乙個演算法必須總是在執行有窮步之後結束。演算法e是滿足這個條件的。因為在步驟e1之後,r的值小於n,所以如果r≠0,下一次進行步驟e1時,n的值已經減小。正整數的遞降序列必然最後要終止。所以,對於任何給定的n的原始值,步驟e1僅僅執行有窮次。然而,注意,步驟的數目可以變成任意大;選擇巨大的m和n,就可能使步驟e1執行百萬次以上。
(具有演算法的其它所有特徵,但卻可能不具有有窮性的步驟,可以叫做「計算方法」。除了給出求兩個整數的最大公因子的演算法外,歐幾里得還給出了實際上等價於演算法e的乙個幾何構造,不過它是求兩個線段的長度的「最大公度」的步驟。這是乙個計算方法,因為如果所給的兩個長度是「無公度的」,那它就不終止)。
2)確定性 演算法的每乙個步驟,必須是確切地定義的。對於每種情況,有待執行的動作必須嚴格地和不含混地規定之。本書中的演算法都應當符合這一準則。但是由於它們是以自然語言描述的,讀者有可能未能精確地理解作者的意思是什麼。為了避免這一困難,就專為描述演算法設計了形式地定義的程式語言或計算機語言,其中每乙個語句有著非常確切的意義。本書中的許多演算法,將同時以自然語言和以一種計算機語言給出。當用乙個計算機語言來描述乙個計算方法時,其表達形式就叫做乙個程式。
在演算法e中,確定性準則意味著:例如應用於步驟e1,則假定讀者確切地知道n除m是什麼意思而餘數又是什麼。事實上,如果m和n不是正整數,那末這究竟是什麼意思,已經沒有一般的約定。-π除-8的餘數是什麼?0除59/13的餘數是什麼?因此,確定性準則意味著我們必須保證,當執行步驟e1時,m和n的值總是正整數。按假設,這在開始時是正確的。而在步驟e1之後,r是乙個非負整數。如果要進行步驟e3,r還必須是非0的,所以m和n事實上總象所要求的那樣,都是正整數。
(未完待續)
計算機程式設計藝術 第I卷 續2
3 輸入 乙個演算法有0個或多個的輸入。它即是,在演算法開始之前,對演算法最初給出的量。這些輸入取自於特定的物件集合。例如,在演算法e中,有兩個輸入,即m和n,它們都取自於正整數集合。4 輸出 乙個演算法有乙個或多個的輸出。它即是,同輸入有某個特定關係的量。演算法e有乙個輸出,即是步驟e2中的n,它...
計算機程式設計藝術 第I卷
本文未完待續 目錄第1章 基本概念 1.1 演算法 1.2 數學準備 1.2.1 數學歸納法 1.2.2 數 冪和對數 1.2.3 和與積 1.2.4 整數函式和初等數論 1.2.5 排列和階乘 1.2.6 二項式係數 1.2.7 調和數 1.2.8 斐波那契數 1.2.9 生成函式 1.2.10 ...
《計算機程式設計藝術》學習筆記(一)
一,mix 儘管knuth 對於mix 不満,在第 3版和他本人的主頁上宣告即將更換為 mmix 但對於初學者如我而言,結全本書先學學 mix似乎更為方便,為了學習 tacop 後面的內容,了解一下 mix看來是必須的步驟。命令格式一般為 op address,i f op為命令關鍵字,addres...