演算法設計與分析 算法學基礎(一) 算法學概述

2021-09-24 06:33:27 字數 3910 閱讀 5894

分類目錄:《演算法設計與分析》總目錄

算法學基礎(二):分析演算法

算法學基礎(三):漸進記號

演算法定義的是計算過程,該過程取某個值或值的集合作為輸入並產生某個值或值的集合作為輸出。這樣演算法就是把輸入轉換成輸出的計算步驟的乙個序列。我們也可以把演算法看成是用於求解良說明的計算問題的工具。一般來說,問題陳述說明了期望的輸入/輸出關係。演算法則描述乙個特定的計算過程來實現該輸入/輸出關係。

例如,我們可能需要把乙個數列排成非遞減序。實際上,這個問題經常出現,並且為引入許多標準的設計技術和分析工具提供了足夠的理由。下面是我們關於排序問題的形式定義:

輸入:n

nn個數的乙個序列(a1

,a2,

⋯,an

)(a_1, a_2, \cdots, a_n)

(a1​,a

2​,⋯

,an​

)輸出:輸入序列的乙個排列(a1

′,a2

′,⋯,

an′)

(a_1', a_2', \cdots, a_n')

(a1′​,

a2′​

,⋯,a

n′​)

,滿足a1≤

a2′≤

⋯≤an

′a_1 \leq a_2' \leq \cdots \leq a_n'

a1​≤a2

′​≤⋯

≤an′

​例如,給定輸入序列(3, 1, 4, 2, 5),排序演算法將返回序列(1, 2, 3, 4, 5)作為輸出。這樣的輸入序列稱為排序問題的乙個例項。一般來說,問題例項由計算該問題解所必需的(滿足問題陳述中強加的各種約束的)輸入組成。

因為許多程式使用排序作為乙個中間步,所以排序是電腦科學中的乙個基本操作。因此,已有許多好的排序演算法供我們任意使用。對於給定應用,哪個演算法最好依賴於以下因素:將被排序的項數、這些項已被稍微排序的程度、關於項值的可能限制、計算機的體系結構,以及將使用的儲存裝置的種類(主存、磁碟或者磁帶)。

若對每個輸入例項演算法都以正確的輸出停機,則稱該演算法是正確的,並稱正確的演算法解決了給定的計算問題。不正確的演算法對某些輸入例項可能根本不停機,也可能以不正確的回答停機。與人們期望的相反,不正確的演算法只要其錯誤率可控有時可能是有用的,但是通常我們只關心正確的演算法。演算法可以用自然語言說明,也可以說明成電腦程式,甚至說明成硬體設計。唯一的要求是這個說明必須精確描述所要遵循的計算過程。

雖然演算法可以解決各式各樣的問題,但是演算法所解決的問題有的兩個共同的特徵:

假設計算機是無限快的並且計算機儲存器是免費的,你還有什麼理由來研究演算法嗎?即使只是因為你還想證明你的解法會終止並以正確的答案終止,那麼回答也是肯定的。如果計算機無限快,那麼用於求解某個問題的任何正確的方法都行。也許你希望你的實現在好的軟體工程實踐的範圍內(例如,你的實現應該具有良好的設計與文件),但是你最常使用的是最容易實現的方法。

當然,計算機也許是快的,但它們不是無限快。儲存器也許是廉價的,但不是免費的。所以計算時間是一種有限資源,儲存器中的空間也一樣。你應該明智地使用這些資源,在時間或空間方面有效的演算法將幫助你這樣使用資源。

為求解相同問題而設計的不同演算法在效率方面常常具有顯著的差別。這些差別可能比由於硬體和軟體造成的差別要重要得多。比如我們常用的兩個用於排序的演算法。第乙個稱為插入排序(在文章《排序演算法(一):插入排序》介紹),為了排序n

nn個項,該演算法所花時間大致等於c1n

2c_1n^2

c1​n

2,其中c

1c_1

c1​是乙個不依賴於n

nn的常數。也就是說,該演算法所花時間大致與n

2n^2

n2成正比。第二個稱為歸併排序(在文章《排序演算法(二):歸併排序》介紹),為了排序n

nn個項,該演算法所花時間大致等於c2n

lg⁡

nc_2n\lg

c2​nlgn

,其中lg⁡n

\lglgn

代表log⁡2

n\log_2

log2​n

且c

2c_2

c2​是另乙個不依賴於n

nn的常數。與歸併排序相比,插入排序通常具有乙個較小的常數因子,所以c

1

c_1c1

​​。我們將看到就執行時間來說,常數因子可能遠沒有對輸入規模n

nn的依賴性重要。把插入排序的執行時間寫成c1n

2c_1n^2

c1​n

2並把歸併排序的執行時間寫成c2n

lg⁡

nc_2n\lg

c2​nlgn

。這時就執行時間來說,插入排序有乙個因子n

nn的地方歸併排序有乙個因子lg⁡n

\lglgn

,後者要小得多。雖然對於小的輸入規模,插入排序通常比歸併排序要快,但是一旦輸入規模n

nn變得足夠大,歸併排序lg⁡n

\lglgn

對n

nn的優點將足以補償常數因子的差別。不管c

1c_1

c1​比c

2c_2

c2​小多少,總會存在乙個交叉點,超出這個點,歸併排序更快。

作為乙個具體的例子,我們讓執行插入排序的一台較快的計算機(計算機a)與執行歸併排序的一台較慢的計算機(計算機b)競爭。每台計算機必須排序乙個具有1000萬個數的陣列。雖然1000萬個數似乎很多,但是,如果這些數是8位元組的整數,那麼輸入將占用大致80mb,即使一台便宜的可攜式計算機的儲存器也能多次裝入這麼多數。假設計算機a每秒執行百億條指令,而計算機b每秒僅執行1000萬條指令,結果計算機a就純計算能力來說比計算機b快1000倍。為使差別更具戲劇性,假設世上最巧妙的程式設計師為計算機a用機器語言編碼插入排序,並且為了排序n

nn個數,結果**需要2n2

2n^2

2n2條指令。進一步假設僅由一位水平一般的程式設計師使用某種帶有乙個低效編譯器的高階語言來實現歸併排序,結果**需要50nlg

⁡n

50n\lg

50nlg

n條指令。為了排序1000萬個數,可以計算得出計算機a需要20000秒,而計算機b只需要1163秒。所以,通過使用乙個執行時間增長較慢的演算法,即使採用乙個較差的編譯器,計算機b比計算機a還快17倍!當我們排序1億個數時,歸併排序的優勢甚至更明顯:這時插入排序需要23天多,而歸併排序不超過4小時。一般來說,隨著問題規模的增大,歸併排序的相對優勢也會增大。

上面的例子表明我們應該像計算機硬體一樣把演算法看成是一種技術。整個系統的效能不但依賴於選擇快速的硬體而且還依賴於選擇有效的演算法。正如其他計算機技術正在快速推進一樣,演算法也在快速發展。

你也許想知道相對其他先進的計算機技術(如:先進的計算機體系結構與製造技術、易於使用、直觀的圖形使用者介面(gui)、物件導向的系統、整合的全球資訊網技術、有線與無線網路的快速組網等),演算法對於當代計算機是否真的那麼重要?回答是肯定的。雖然某些應用在應用層不明確需要演算法內容(如某些簡單的基於全球資訊網的應用),但是許多應用確實需要演算法內容。例如,考慮一種基於全球資訊網的服務,它確定如何從乙個位置旅行到另乙個位置。其實現依賴於快速的硬體、乙個圖形使用者介面、廣域網,還可能依賴於物件導向技術。然而,對某些操作,如尋找路線(可能使用最短路徑演算法)、描繪地圖、插入位址,它還是需要演算法。而且,即使是那些在應用層不需要演算法內容的應用也高度依賴於演算法。該應用依賴於快速的硬體嗎?硬體設計用到演算法。該應用依賴於圖形使用者介面嗎?任何圖形使用者介面的設計都依賴於演算法。該應用依賴於網路嗎?網路中的路由高度依賴於演算法。該應用採用一種不同於機器**的語言來書寫嗎?那麼它被某個編譯器、直譯器或彙編器處理過,所有這些都廣泛地使用演算法。

演算法是當代計算機中使用的大多數技術的核心。

進一步,隨著計算機能力的不斷增強,我們使用計算機來求解比以前更大的問題。正如我們在上面對插入排序與歸併排序的比較中所看到的,正是在較大問題規模時,演算法之間效率的差別才變得特別顯著。是否具有演算法知識與技術的堅實基礎是區分真正熟練的程式設計師與初學者的乙個特徵。使用現代計算技術,如果你對演算法懂得不多,你也可以完成一些任務,但是,如果有乙個好的演算法背景,那麼你可以做的事情就多得多。

演算法學習筆記(一)演算法基礎

一 資料結構和演算法的關係 資料 data 結構 structure 是一門研究組織資料方式的學科,有了程式語言也就有了資料結構.學好資料結構編寫出更加漂亮,更加有效率的 要學習好資料結構就要多多考慮如何將生活中遇到的問題,用程式去實現解決 程式 資料結構 演算法 資料結構是演算法的基礎,換言之,想...

演算法導論 一 演算法基礎

迴圈不變式主要用來幫助我們理解程式的正確性。迴圈不變式的三條性質 初始化 迴圈的第一次迭代之前,它為真。保持 如果迴圈的某次迭代之前它為真,那麼下次迭代之前它仍為真。終止 再迴圈終止時,可驗證演算法的正確性。偽 的重要性在於它可以簡潔地表達出演算法的本質 縮排代表塊結構 採用縮排代表塊結構可以大大提...

(一)演算法基礎 排序演算法

作為演算法的入門,排序演算法再合適不過了,在這裡我主要介紹四種排序演算法 插入排序 歸併排序 快速排序以及希爾排序。不過在介紹這些演算法之前,我們先來做一些準備工作。一 演算法測試函式 在實現乙個演算法後,必然要對這個演算法進行除錯和分析,這裡我寫了一些函式用於演算法的測試。隨機數組生成函式 int...