從食譜到高階程式語言 國立中央大學 資工系 江振瑞

Slides:



Advertisements
Similar presentations
大綱 1. 三角函數的導函數. 2. 反三角函數的導函數. 3. 對數函數的導函數. 4. 指數函數的導函數.
Advertisements

第一單元 建立java 程式.
Course 1 演算法: 效率、分析與量級 Algorithms: Efficiency, Analysis, and Order
基本概論 Basic concepts.
Introduction to C Programming
Introduction 基本概念 授課老師:蕭志明
第一章 資料結構導論 1-1 資料結構簡介 1-2 認識程式設計 1-3 演算法效能分析 1-4 物件導向程式設計與Java.
Performance Evaluation
1 認識演算法 從食譜到高階程式語言.
The discipline of algorithms
第四章 數列與級數 4-1 等差數列與級數 4-2 等比數列與級數 4-3 無窮等比級數 下一頁 總目錄.
TQC+ JAVA全國教師研習會 PLWeb 程式設計練習平台 簡介.
Chapter 5 遞迴 資料結構導論 - C語言實作.
Chapter 5 迴圈.
程式設計概論 1.1 程式設計概論 程式語言的演進 物件導向程式 程式開發流程 1.2 C++開發工具
從食譜到高階程式語言 中央大學 資工系 江振瑞 教授
第1章 認識Arduino.
2-3 基本數位邏輯處理※.
Analysis of Sorting Algorithms
4B冊 認識公倍數和最小公倍數 公倍數和最小公倍數的關係.
SQL Stored Procedure SQL 預存程序.
R教學 安裝RStudio 羅琪老師.
ASP.NET基本設計與操作 建國科技大學 資管系 饒瑞佶 2007年.
安裝JDK 安裝Eclipse Eclipse 中文化
Introduction.
程式設計專題.
資料結構 第1章 導論.
Java 程式設計 講師:FrankLin.
1.3 在整除性問題之應用 附加例題 3 © 文達出版 (香港 )有限公司.
Chap3 Linked List 鏈結串列.
第一章 直角坐標系 1-1 數系的發展.
程式設計實習課(四) ----C 函數運用----
演算法複雜度分析 決勝在數大時 中央大學 資工系 江振瑞 教授.
第一單元 建立java 程式.
分支宣告與程式設計 黃聰明 國立臺灣師範大學數學系
第二次電腦實習課 說明者:吳東陽 2003/10/07.
鄧姚文 資料結構 第一章:基本概念 鄧姚文
Week 2: 程式設計概念與 演算法的效能評估
學習單元:N6 數的性質 學習單位:N6-3 用短除法求H.C.F. 和 L.C.M. 學習重點 : 1. 複習因數分解法求
The discipline of algorithms 有關演算法的一門學科
Introduction to C Programming
Definition of Trace Function
小學四年級數學科 8.最大公因數.
第一次Labview就上手 參考書籍: LabVIEW for Everyone (Jeffrey Travis/Jim Kring)
CH05. 選擇敘述.
大綱:加減法的化簡 乘除法的化簡 去括號法則 蘇奕君 台灣數位學習科技股份有限公司
挑戰C++程式語言 ──第8章 進一步談字元與字串
C qsort.
實作輔導 本周5/5(六)安排實作輔導 二時段: 周六 11:00~12:30 周六13:30~15:30.
資料結構簡介 綠園.
演算法分析 (Analyzing Algorithms)
流程控制:Switch-Case 94學年度第一學期‧資訊教育 東海大學物理系.
MiRanda Java Interface v1.0的使用方法
10394: Twin Primes ★★★☆☆ 題組:Problem Set Archive with Online Judge
例題 1. 多項式的排列 1-2 多項式及其加減法 將多項式 按下列方式排列: (1) 降冪排列:______________________ (2) 升冪排列:______________________ 排列 降冪:次數由高至低 升冪;次數由低至高.
使用VHDL設計-8x3編碼電路 通訊一甲 B 楊穎穆.
10328: Coin Toss ★★★☆☆ 題組:Problem Set Archive with Online Judge
1757: Secret Chamber at Mount Rushmore
13194: DPA Number II ★★☆☆☆ 題組:Problem Set Archive with Online Judge
資料表示方法 資料儲存單位.
第一章 直角坐標系 1-3 函數及其圖形.
All Sources Shortest Path The Floyd-Warshall Algorithm
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
String類別 在C語言中提供兩種支援字串的方式 可以使用傳統以null結尾的字元陣列 使用string類別
10303: How Many Trees? ★★☆☆☆ 題組:Contest Archive with Online Judge
Chapter 4 Multi-Threads (多執行緒).
11621 : Small Factors ★★☆☆☆ 題組:Problem Set Archive with Online Judge
第三章 比與比例式 3-1 比例式 3-2 連比例 3-3 正比與反比.
ABAP Basic Concept (2) 運算子 控制式與迴圈 Subroutines Event Block
Presentation transcript:

從食譜到高階程式語言 國立中央大學 資工系 江振瑞 1 認識演算法 從食譜到高階程式語言 國立中央大學 資工系 江振瑞

1.1 演算法名稱的由來

阿布‧賈法‧穆罕默德‧賓‧穆薩‧阿爾-可瓦里茲米 演算法(Algorithm)一詞源自一位的阿拉伯數學家: 阿布‧賈法‧穆罕默德‧賓‧穆薩‧阿爾‧可瓦里茲米(Abu Jafar Muhammad ibn Musa al-Khwarizmi) (780 – 850 A. D.)的拉丁文譯名(al-Khwarizmi )。

阿爾-可瓦里茲米 (al-Khwarizmi) 阿爾–可瓦里茲米將印度所發明的十進位數字記號傳入阿拉伯地區,而阿拉伯商人在經商時則將十進位數字記號傳入歐洲成為現今我們使用的數字記號。 更重要的是,阿爾–可瓦里茲米著有一本討論有系統地解決一次方程式 (linear equation) 及一元二次方程式 (quadratic equation) 的書籍,此書被翻譯成名為”Liber algebrae et almucabala”的拉丁文書籍,啟發了代數學的萌芽,對人類的現代科技與文明發展有相當深遠的影響。代數學的英文名稱 Algebra 就是源自於此書之書名。

阿爾-可瓦里茲米 (續) (al-Khwarizmi) 阿爾–可瓦里茲米以一步一步(step by step)的方式,描述算術(arithmetic)運算與一元二次方程式及某些一次方程式的解答過程。稍後我們會知道,這些一步一步描述的解答過程就是我們現今所定義的演算法。這就是為什麼演算法(algorithm)的名稱是源自於阿爾–可瓦里茲米(al-Khwarizmi)的原因。 以下我們展示 一元二次方程的解法描述:

一元二次方程解法描述 關於解開 x 的方程: ax2 + bx + c = 0 (ax2 + bx = c) 的解法

1.2 什麼是演算法?

什麼是演算法? 廣義的說,演算法是解決某一問題的一步一步程序(a step-by-step procedure for solving a problem) -- Merriam-Webster Dictionary 狹義的說,演算法是一個由一些步驟所構成的集合,依循這些步驟得以解決數學問題或完成計算機進程(a set of steps that are followed in order to solve a mathematical problem or to complete a computer process) -- Merriam-Webster Dictionary

食譜 (recipe) 是演算法 出處:嘉義市政府衛生局網頁 (http://www.cichb.gov.tw/other/bus_detail.asp?bus_dtl_id=1066)

流程圖 (flowchart) 是演算法

其他廣義演算法的例子 企業組織的標準作業程序(Standard Operating Procedure, SOP) 也是演算法 工作人員面對特定問題時,只要按照步驟指示一步一步進行就能解決問題 設備的使用手冊(manual)或故障排除手冊也包含許多演算法 因為它包含許多可以用於解決某一問題(例如,如何安裝新設備及解決印表機的卡紙問題等) 的一步一步程序。

演算法計算角度的嚴謹定義 演算法(algorithm): 由有限(finite)步驟(step)所構成的集合,依照給定輸入(input)依序執行每個明確(definite)且有效(effective) 的步驟,以便能夠解決特定的問題;而步驟的執行必定會終止(terminate),並產生輸出(output) 。

演算法的特性 根據演算法計算角度的嚴謹定義,演算法是由解決某一特定問題的步驟所組成,具有以下特性: 指定輸入(input):演算法必須指定輸入,可以由外界輸入0 個、1 個或多個資料。 具有輸出(output):演算法必定有至少1 個以上的輸出。 有限性(finiteness):演算法步驟的個數必須是有限的,而且步驟的執行最後會終止(terminate)。 明確性(definiteness):演算法的每個步驟都必須是明確(definite) 而不含糊的(unambiguous)。 有效性(effectiveness):演算法的每個步驟必須是有效的(effective) 或說是可行的(feasible)。 Definiteness: each instruction is clear and unambiguous. To compute “add 6 or 7 to x” is not definite. Finiteness: (the number of steps should be finite and the execution of the steps should terminate) if we trace out the instructions of an algorithm, then for all cases, the algorithm terminates after a finite number of steps. Finiteness implies termination (OS is thus not an algorithm) Effectiveness: All operations should be feasible. Each step must be such that it can, at least in principle, be done by a person using pencil and paper in a finite amount of time.

演算法的特性(續) 演算法必須指定輸入 演算法必須要有一個以上的輸出 演算法必須滿足有限性(finiteness) 我們有時會透過介面(例如鍵盤) 由外界獲得問題輸入,有時也會將輸入直接寫在演算法中。 因此演算法可以由外界輸入0 個、1 個或多個資料。 演算法必須要有一個以上的輸出 如此演算法才能為人們所用。 演算法必須滿足有限性(finiteness) 它的步驟個數必須是有限的,而且步驟的執行最後會終止(terminate); 如此,演算法才有可能在執行有限步驟之後終止並產生輸出為人們所用。 Definiteness: each instruction is clear and unambiguous. To compute “add 6 or 7 to x” is not definite. Finiteness: (the number of steps should be finite and the execution of the steps should terminate) if we trace out the instructions of an algorithm, then for all cases, the algorithm terminates after a finite number of steps. Finiteness implies termination (OS is thus not an algorithm) Effectiveness: All operations should be feasible. Each step must be such that it can, at least in principle, be done by a person using pencil and paper in a finite amount of time.

演算法的特性(續) 演算法必須滿足明確性(definiteness),也就是說它的每一個步驟必須是明確(definite) 而不含糊的(unambiguous)。 例如,若有一個步驟是「加6 或7 到變數x」,則這個步驟是不明確的,因為我們可能加6 也可能加7 到變數x 中 但是,「加亂數生成器(random number generator) 函數的值到變數x」則是明確的步驟,因為我們可以很明確地將亂數產生器函數的值加到變數x 又例如,「計算5/0」是不明確的,因為分母(除數) 為0 是沒有明確定義的計算。 Definiteness: each instruction is clear and unambiguous. To compute “add 6 or 7 to x” is not definite. Finiteness: (the number of steps should be finite and the execution of the steps should terminate) if we trace out the instructions of an algorithm, then for all cases, the algorithm terminates after a finite number of steps. Finiteness implies termination (OS is thus not an algorithm) Effectiveness: All operations should be feasible. Each step must be such that it can, at least in principle, be done by a person using pencil and paper in a finite amount of time.

演算法的特性(續) 演算法必須滿足有效性(effectiveness),也就是說演算法的每個步驟必須是有效的(effective)或說是可行的(feasible) 每個步驟即使由人們拿著紙筆,都可以在有限時間內計算出結果。 例如,步驟「計算出√2 完全無誤差的值」不滿足有效性,因為它是不可行的,我們需要進行無窮位數的計算才可以得到√ 2 完全無誤差的值。 相反的,「計算√2 到小數點以下10 位,並捨棄其後位數」則滿足有效性,因為它是可行的,人們即使只是藉由紙筆,都可以計算出√2 = 1.4142135623 的結果。 Definiteness: each instruction is clear and unambiguous. To compute “add 6 or 7 to x” is not definite. Finiteness: (the number of steps should be finite and the execution of the steps should terminate) if we trace out the instructions of an algorithm, then for all cases, the algorithm terminates after a finite number of steps. Finiteness implies termination (OS is thus not an algorithm) Effectiveness: All operations should be feasible. Each step must be such that it can, at least in principle, be done by a person using pencil and paper in a finite amount of time.

1.3 演算法的例子

解決正整數m是不是正整數n的倍數問題的流程圖

解決正整數m是不是正整數n的倍數問題的C++程式

一個既不會終止,也不產生輸出的C程式 – 不是演算法

1.4 如何表示演算法?

演算法的表示 一般我們使用以下方式來表示演算法: 自然語言(中文或英文等語言) 流程圖(flow chart) 虛擬碼(pseudo code) 以下我們舉一個古老的演算法  歐幾里德演算法(Euclid Algorithm)為例子,說明如何以上列三種方式表示演算法。

歐幾里德演算法(輾轉相除法) 歐幾里德演算法大約在西元前300年由希臘數學家歐幾里德提出,可用於求出二個整數的最大公因數(GCD, Greatest Common Divisor),又稱為輾轉相除法。

歐幾里德演算法 (Euclid Algorithm) 以自然語言表示 歐幾里德演算法: 問題:給定二個正整數m及n,找出此二數的最大公因數GCD(也就是能同時整除m及n的最大正整數) 解法: 步驟1.[找出餘數] 求出m除以n的餘數,並記錄於r。 步驟2.[餘數為0嗎?] 如果r=0則停止,輸出n為GCD。 步驟3.[換被除數與除數] 設定m=n,n=r,並跳至步驟1。

歐幾里德演算法 (Euclid Algorithm) 以流程圖表示

ANSI流程圖符號

ANSI流程圖符號(續)

歐幾里德演算法 (Euclid Algorithm) 以虛擬碼表示

虛擬碼 (Pseudo Code) 虛擬碼以一種混雜著自然語言與高階程式語言結構的方式來描述演算法。 可以達到簡潔易讀、容易分析,而且也容易轉換為高階程式語言的目的。 以下我們介紹本課程所採用的虛擬碼撰寫規則,因為虛擬碼仍然具有自然語言性質,因此這些撰寫規則有時可以稍稍調整,以方便閱讀者理解為原則。

虛擬碼 (Pseudo Code)(續) 虛擬碼撰寫規則如下: 演算法名稱及參數:以 Algorithm 演算法名稱 (參數 1, 參數 2,…) 來列出演算法名稱並指明其輸入參數。 輸入:以 Input 輸入描述 來進行輸入說明 輸出:以 Output 輸出描述 來進行輸出說明

虛擬碼 (Pseudo Code)(續) 設定:以 ← 表示,可以將一個算式 (expression) 的值指定給某一個變數 (置入某變數中) 算術運算:以 + − ∗ / % 表示加、減、乘、除、模 (除法求餘數)運算

虛擬碼 (Pseudo Code)(續) 比較與邏輯運算:以 = > <    表示等於、大於、小於、大於等於、小於等於及不等於的運算,並使用   ~ 表示邏輯的且、或與反向的運算。 決策結構:以 if 條件 then 條件為真的動作 else 條件為偽的動作 end if 來表示。當條件成立時,演算法執行所有包含在「條件為真的動作」的所有指令 (步驟);反之則執行所有包含在「條件為偽的動作」的所有指令。

虛擬碼 (Pseudo Code)(續) while 迴圈:以 while 條件 do 迴圈動作 end while 來表示。當條件成立時,演算法會重複執行所有包含在「迴圈動作」的所有指令;反之則離開迴圈,進入下一個指令。 for 迴圈:以 for 迴圈變數變動之範圍及其變動方式do 迴圈動作 end for 來表示。當「迴圈變數」的值在指定的範圍中時,演算法會重複執行所有包含在「迴圈動作」的所有指令;反之則離開迴圈,進入下一個指令。

虛擬碼 (Pseudo Code)(續) 陣列元素索引:以 陣列名稱 [i] 代表命名為陣列名稱的陣列索引 (index) 為 i 的元素,一個有 n 個元素的陣列,其元素索引值為 0,1,…,n-1 演算法呼叫:以 演算法名稱 (參數…) 來表示演算法的呼叫 演算法返回: 以 return 返回值 來代表演算法結束執行並輸出返回值。

1.5 如何實作演算法?

演算法的實作(implementation) 除了以自然語言、流程圖、虛擬碼表示演算法之外,我們也可以使用高階程式語言(high level programming language)來表示演算法。 當我們以高階程式語言表示演算法時,我們可以在電腦上直接執行以高階程式語言編寫而成的程式,並藉此得到執行結果。我們特別將之稱為「以高階程式語言實作(implement)演算法」,或是稱為「以高階程式語言進行演算法的實作(implementation)」。

演算法的實作(implementation)(續) 以下我們將舉例說明使用C、C++、Java 與Python 語言實作歐幾里德演算法,或稱為歐幾里德GCD(Euclid GCD)演算法。 建議使用Jeep5軟體進行C、C++、Java 與Python 語言的編輯、編譯與執行。Jeep5 為Java Editor for Chinese Programmer v5.0 的簡稱,是由江振瑞教授使用Java 語言所編寫的整合開發環境(Integrated Development Environment, IDE)軟體,支援C、C++、Java 與Python 四種語言,並透過簡潔的中文介面,讓使用者輕易完成四種不同語言程式的編輯、編譯與執行等工作。請參考本單元後的Jeep5補充資料以獲得Jeep5詳細的資訊 。

演算法的實作(implementation)(續) 因為演算法有指定輸入的特性,因此演算法僅處理特定的輸入。例如,剛剛提過的歐幾里德演算法指定輸入二個正整數m及n。當然,當我們以高階程式語言實作演算法,讓使用者特過輸入介面由外界傳入演算法輸入時,可能會輸入錯誤的資料(例如輸入負數)。本課程聚焦於演算法解決問題的核心概念,因而假設所有的輸入都符合演算法的指定,所以在實作演算法時不處理使用者輸入錯誤資料的狀況。

演算法的實作(implementation)(續) 在實務上,若我們將符合演算法指定的輸入資料以文字檔案形式儲存,並以作業系統之輸入轉向(redirect)方式將文字檔案資料直接輸入高階程式語言程式中,則所有的輸入都會符合演算法的指定。基本上,著名的計算機協會國際大學生程式設計競賽(Association of Computing Machinery International Collegiate Programming Contest,簡稱 ACM ICPC)就是採取上述的方法作為高階語言程式的輸入方式以進行比賽。 ACM ICPC是一個試煉各種演算法實作的好場合,國際間也有許多團體提供相關的ICPC訓練教學網站(例如,UVa Online Judge)與ICPC賽事裁判系統(例如,PC2(Programming Contest Control)系統)。請參考本單元後的補充資料以獲得ACM ICPC詳細的資訊 。

以C程式語言實作 歐幾里德GCD演算法 下列的C語言程式EuclidGCD.c以int EuclidGCD(int m, int n)函式或函數(function)實作歐幾里德演算法。並在主要函式main()中加入標準輸入(printf(...))與標準輸出(scanf(...))敘述,並呼叫EuclidGCD方法,讓演算法可以由外部輸入訊息,並輸出計算結果。

以C程式語言實作 歐幾里德GCD演算法–執行結果 實作展示: 以Jeep5編輯、編譯及執行 執行結果:

以C++程式語言實作 歐幾里德GCD演算法 下列的C++語言程式EuclidGCD.cpp以int EuclidGCD(int m, int n)函式實作歐幾里德演算法。並在主要函式int main()中加入標準輸入(cin>>...)與標準輸出(cout<<...)敘述,並呼叫EuclidGCD方法,讓演算法可以由外部輸入訊息,並輸出計算結果。

以C++程式語言實作 歐幾里德GCD演算法–執行結果 實作展示: 以Jeep5編輯、編譯及執行 執行結果:

以Java程式語言實作 歐幾里德GCD演算法 下列的Java語言程式EuclidGCDClass1.java以方法(method)static int EuclidGCD(int m, int n)實作了歐幾里德演算法。並在主類別EuclidGCDClass1中的主要方法public static void main(…)中加入標準輸入(Scanner Cin=new Scanner(System.in);...Cin.nextInt())與標準輸出(System.out.print(...))敘述,並呼叫EuclidGCD方法,讓演算法可以由外部輸入訊息,並輸出計算結果。

以Java程式語言實作 歐幾里德GCD演算法–執行結果 實作展示: 以Jeep5編輯、編譯及執行 執行結果:

以Java程式語言實作 歐幾里德GCD演算法(續) 下列的Java語言程式EuclidGCDClass2.java與EuclidGCDClass1.java類似,但是採用視窗輸入(JOptionPane.showInputDialog(...))與視窗輸出(JOptionPane.showMessageDialog(...))敘述,讓演算法可以由外部輸入訊息,並輸出計算結果。 另外,程式EuclidGCDClass2.java也善用Java語言支援UTF-8字元編碼的國際化特性,使用中文來替變數命名。這樣做有一個好處,當讀者看到程式中出現中文的變數名稱時,就可以知道這是由使用者自行命名的,而英文的部份則很清楚的是語言的關鍵字或是語言系統中內建的類別。

以Java程式語言實作 歐幾里德GCD演算法–執行結果(續) 實作展示: 以Jeep5編輯、編譯及執行 執行結果:

以Python程式語言實作 歐幾里德GCD演算法 下列的Python語言程式EuclidGCD.py以EuclidGCD(m, n)方法實作歐幾里德演算法。並以標準輸入(raw_input(...))與標準輸出(print(...))敘述由外部輸入訊息及輸出計算結果。

以Python程式語言實作 歐幾里德GCD演算法–執行結果 實作展示: 以Jeep5編輯及執行 執行結果:

1.6 演算法的正確性與效能

演算法的正確性與效能 一個演算法一定要能夠產生正確的結果,也就是滿足正確性(correctness),如此演算法才能夠為人們所用。 我們可以透過一些證明的技術,如歸納法(induction)或矛盾法(contradiction)等來證明演算法的正確性。 演算法的正確性無疑的是演算法最重要的特性,然而因為課程時間有限,我們有時不會特別說明演算法的正確性證明。請大家自行參考文獻資料以獲得演算法的正確性證明。

演算法的正確性與效能(續) 除了演算法的正確性之外,我們也關心演算法的效能(efficiency),也就是討論演算法是否能夠使用較少資源而有效率地執行。 演算法執行時使用的資源有: 時間資源 記憶體資源 網路頻寬資源(當演算法需要透過網路傳輸資料時) 邏輯閘資源(當演算法使用邏輯閘實作時) … 在本課程我們聚焦於討論演算法執行時使用的時間資源與記憶體資源。

演算法的正確性與效能(續) 若我們能夠將所有的演算法以相同的高階程式語言實作,並在相同條件下於相同的電腦上執行,則我們似乎可以利用高階程式語言程式的執行時間與執行時所佔用的記憶體空間來衡量被實作演算法的效能。 不過,這太不實際了,而且藉由這個方式我們也難以直接看出演算法效能與演算法輸入規模(input size)的關係。 我們需要其他更具學理基礎,而且更容易分析與比較的方式來衡量演算法的效能

演算法的正確性與效能(續) 在學理上,我們使用: 來分析演算法的執行時間與佔用的記憶體空間。 時間複雜度(time complexity) 空間複雜度(space complexity) 來分析演算法的執行時間與佔用的記憶體空間。 以下我們先說明演算法的時間複雜度概念,而演算法空間複雜度則是類似的概念,我們在稍後演算法複雜度分析範例時會一併說明。

1.7 演算法的時間複雜度

演算法的時間複雜度 演算法的時間複雜度分為以下三種狀況: 最佳狀況(best case)時間複雜度: 考慮演算法執行時所需要的最少執行步驟數。 最差狀況(worst case)時間複雜度: 考慮演算法執行時所需要的最多執行步驟數。 平均狀況(average case)時間複雜度: 考慮所有可能狀況下演算法執行時所需要的平均步驟數。 在上列的定義中,所謂的一個步驟(step)指的是演算法中的基本操作(operation),如算術運算操作(arithmatic operation) 及邏輯運算操作(logic operation) 等。有時候我們也會將幾個連續執行的操作視為一個步驟,我們稍後會有詳細的說明。

演算法的時間複雜度(續) 在所有的時間複雜度之間,最差狀況時間複雜度是很重要的一項。 一般我們會先分析演算法的最差狀況時間複雜度,因為這項複雜度可以讓我們了解演算法在最壞的情況下的執行時間概況。 例如,假設我們需要使用一些天氣預測演算法來預測三天後的天氣慨況,並在一小時之後發佈預測結果。姑且不論演算法預測天氣的準確度,若我們能夠分析出這些演算法的最壞情況時間複雜度,則大約可以推估出哪些演算法即使在最壞情況下也可以在一小時之內執行完畢,讓我們來得及發佈天氣預測結果。

演算法的時間複雜度(續) 針對一些我們平日經常使用的演算法,會面臨各種不同狀況,執行步驟有時多有時少,而碰到最壞與最好狀況的機率又很低,則此演算法的平均狀況時間複雜度也很重要。 例如,我們經常使用排序(sorting)演算法將一連串的雜亂數字依由小到大的次序排好順序,或將一連串的雜亂檔案名稱依照字典順序(lexical order,如字典版將單字由A開頭排到Z開頭的順序)排列。因為可能面對不同的一連串數字與檔名,因而此時我們最關心排序演算法的平均時間複雜度。

演算法的時間複雜度(續) 相對於最差狀況時間複雜度與平均狀況時間複雜度,演算法的最佳狀況時間複雜度則顯得較不重要。 這是因為一方面最佳狀況出現的機率不高,而另一方面則是因為有時最佳狀況時間複雜度是顯而易見的(trivial)。 例如,我們直接利用質數(prime number or prime)的定義設計一個演算法,檢查一個輸入的大於2的正整數n是不是質數。依照定義,我們只要由整數2開始到n之間(不含n)找到任何一個n的因數(factor),就可以判斷n不是質數了。對於這個演算法,若我們輸入任何大於2的偶數n,都可以馬上找出2是n的因數而判斷n不是質數,這對應演算法的最佳狀況時間複雜度,這顯然是顯而易見的。

演算法的時間複雜度(續) 一般而言,演算法的平均狀況時間複雜度最難分析(求出),最壞狀況時間複雜度稍微容易些,而最佳狀況時間複雜度最容易分析。 我們通常會分析演算法的最壞狀況時間複雜度,若有可能,還會分析演算法的平均狀況時間複雜度,但是常常不分析最佳狀況時間複雜度。

演算法的時間複雜度(續) 一個檢查大於2正整數是否為質數的演算法

演算法的時間複雜度(續) 針對PrimeCheck1演算法,我們可以看出: 輸入大於2 的任意正整數n,若n 是質數,則演算法PrimeCheck1 需要執行整數除法求餘數(也就是n%i) 操作與整數比較(也就是(n%i)=0) 操作各n-2 次,才可以知道n 是質數。 另外,若n 不是質值,則演算法PrimeCheck1 只要執行整數除法求餘數操作與整數比較操作1 次就可以知道n 不是質數。 因此,我們很容易看出來,在最壞狀況下,演算法PrimeCheck1 的執行時間與輸入的正整數n 成正比;而在最佳狀況下,演算法PrimeCheck1 的執行時間大約為執行1次整數除法求餘數與整數比較操作,而與輸入的正整數n 大小無關。 也就是說,演算法PrimeCheck1 的最差狀況時間複雜度為n − 2,而演算法PrimeCheck1 的最佳狀況時間複雜度為1。 至於演算法PrimeCheck1 的平均狀況時間複雜度分析比較複雜,在此我們省略不提。

1.8 大O漸近記號

漸近記號 一般我們使用漸近記號(asymptotic notation)表示演算法的複雜度(complexity)。 漸近記號考慮演算法在處理資料範圍或輸入規模(input size)或問題規模(problem size)足夠大時的複雜度。 使用漸近記號原因之一為聚焦於輸入規模足夠大的狀況: 一般而言,在演算法的輸入規模較小時,不管是有效率(複雜度較低)或沒有效率(複雜度較高)的演算法通常都可以很快的執行完畢。相對的,在演算法的輸入規模非常大時,有效率的演算法還是可以在一定的時間內執行完畢,而沒有效率的演算法則可能需要相當長的時間,甚至於需要經年累月才能結束。因此,在分析演算法時,我們會聚焦於演算法輸入規模足夠大的狀況,這是為什麼分析演算法複雜度需要採取漸近記號的原因之一。

漸近記號(續) 使用漸近記號原因之二為簡化個別步驟認定: 當我們分析演算法時,認定不同的個別步驟經常會產生不同的時間複雜度。 例如,當我們將演算法PrimeCheck1中的整數除法求餘數操作與整數比較操作視為一個步驟時,我們說演算法PrimeCheck1的最差狀況時間複雜度為n-2。當我們將整數除法求餘數操作與整數比較操作視為二個個別步驟時,我們說演算法PrimeCheck1的最差狀況時間複雜度為2n-4。而當我們另外將迴圈控制中的迴圈變數遞增與迴圈變數與迴圈結束值的比較再視為另外二個個別步驟時,則我們可以說演算法PrimeCheck1的最差狀況時間複雜度為4n-8。 以上這些時間複雜度的說法,似乎都正確。當我們使用漸近記號來表示演算法複雜度的情況下,這些說法是完全相通的;更精確地說,它們使用漸近記號來表示時是完全相同的。這簡化個別步驟的認定,是我們使用漸近記號來表示演算法複雜度的原因之二。

漸近記號(續) 在演算法處理資料範圍或輸入規模足夠大時,演算法的執行時間複雜度會漸近於一個量級(order)。 一般而言,演算法的執行時間複雜度是一個輸入規模n的多項式,當演算法輸入規模足夠大時,時間複雜度多項式中除了最高次方的項目外,其他的部分都可以被忽略;而同時,最高次方項目的常數係數也同時可以被忽略。

漸近記號(續) 例如,若一個演算法的時間複雜度為n-2、2n-4或4n-8,則當n足夠大時,此演算法的時間複雜度漸近於n,屬於一次方或線性(linear)量級。 又例如,若一個演算法的時間複雜度為35n2+12n+11,則當n足夠大時,此演算法的時間複雜度漸近於n2,屬於平方(quadratic)量級。 而若一個演算法的時間複雜度為28n3+1245n2+162n+321,則當n足夠大時,此演算法的時間複雜度漸近於n3,屬於立方(cubic)量級。 一般而言,若是一個演算法的時間複雜度表示為一個多項式,則我們取這個多項式的最高次方為其時間複雜度的量級,並且將此量級以大O記號表示。以下我們詳細介紹大O記號。

大O記號 大O記號(Big-O notation)為一種漸近記號,我們通常使用大O記號來表示演算法在輸入規模足夠大時,其複雜度的量級漸近情形, 以下我們正式定義大O記號: [定義] 大O記號(Big-O notation): (O 代表order 之意) 令f(n) 與g(n) 是由非負整數對應至實數的函數,若存在正實數常數c 和正整數常數n0,使得對所有的n  n0 而言,f(n) ≤ cg(n) 成立,則我們說f(n) =O(g(n))。 因為O(g(n))帶有集合的涵義,因此f(n) =O(g(n))唸作「f(n) 屬於Big-O of g(n)」;而比較完整的英文唸法為「f of n is of Big-O of g of n」)。

大O記號(續) 例如,令f(n)=2n2 + n + 3 ,g(n) = n2。因為存在c = 6 和n0 = 1,使得當n  n0 = 1 時, f(n)= 2n2 + n + 3 ≤ cn2 = 6n2 =6g(n)成立,所以我們說f(n)=2n2 + n + 3 =O(g(n))=O(n2)。 我們可以由圖看出,當n  n0 = 1 時, f(n)  6g(n)成立,我們稱g(n)是f(n)的漸近上界(asymptotic upper bound)。這表示g(n)的成長率比f(n)還快或一樣快。

一些複雜度的大 O 記號表示及其量級

1.9 降低演算法時間複雜度量級

演算法時間複雜度量級比較 我們首先比較不同演算法時間複雜度在各種不同量級之下的執行步驟數。 某些量級在演算法的輸入規模還不是很大的情況下,演算法時間複雜度的對照數值(執行步驟數)就已經相當大了,這表示演算法需要執行相當久的時間。 例如,若演算法的一個步驟(如比較兩個整數的操作)需要一個微秒(s, micro second,百萬分之一秒)來完成,則當輸入規模n 為32 時,平方量級演算法需要1,024 微秒≈0.001 秒來完成,而指數量級演算法需要2n=4,294,967,296 微秒≈143 分鐘來完成。

演算法時間複雜度量級比較(續) 演算法時間複雜度量級高低的次序 (靠左邊的量級是比較低,成長比較慢的量級): O(1),O(log n),O(√n),O(n),O(n log n),O(n2),O(n3),O(2n) 設計一個時間複雜度量級比較低的演算法是我們必須一直擺在心中的目標。

測試質數的一個觀察 以下,我們再舉檢查一個正整數是否為質數的演算法為例,來看看演算法的量級對執行時間的影響。 利用以下的觀察,我們可以設計出一個量級比直接使用質數定義設計的演算法的量級更低的演算法。 [觀察] 對於任意的大於2 的整數n 而言,若所有小於或等於 √n 的整數(1 除外)都無法整除n,則n 是一個質數。

觀察實例 每個整數n的因數都是成對出現的,例如,16的因數為1與16(116=16)、2與8(28=16)、4與4(44=16)。成對出現的因數中,一個會小於等於n的平方根,而另一個則是大於等於n的平方根,因此,我們只要檢查小於等於n的平方根的所有不等於1的整數中是否有n的因數就可以知道n是不是質數了。

另一個用以測試質數的演算法 我們根據上列觀察設計出另一個演算法PrimeCheck2

質數檢查演算法複雜度量級比較 最差狀況(worst case)時間複雜度: 最佳狀況(best case)時間複雜度: PrimeCheck1: n-2=O(n),屬於線性量級。 PrimeCheck2: √n − 1 =O(√n),屬於平方根量級。 最佳狀況(best case)時間複雜度: PrimeCheck1: 1=O(1),屬於常數量級。 PrimeCheck2: 1=O(1),屬於常數量級。 在最差狀況下,平方根量級的演算法PrimeCheck2的執行步驟數將比線性量級的演算法PrimeCheck1的執行步驟數少很多。例如,若輸入的n 為2147483647,則演算法PrimeCheck1 需要執行n-2=2147483645次整數除法求餘數和整數比較敘述才可以得知2147483647是質數,而演算法PrimeCheck2只要執行√n-1= 46339次,二者的差距約為√n=46340 倍。當然,當所輸入的n越大時,這種差距越大。

以Java程式語言實作 PrimeCheck1演算法 下列的Java語言程式PrimeCheck1.java實作PrimeCheck1演算法,並使用System.currentTimeMillis()測量程式執行時間:

PrimeCheck1.java執行結果

以Java程式語言實作 PrimeCheck2演算法 下列的Java語言程式PrimeCheck2.java實作PrimeCheck2演算法,並使用System.currentTimeMillis()測量程式執行時間: :

PrimeCheck2.java執行結果

1.10 多項式時間、指數時間及 偽多項式時間演算法

多項式時間演算法 若一個演算法的時間複雜度為多項式函數,則其為 範例: 費伯納西數列演算法 多項式時間複雜度演算法(polynomial time-complexity algorithm) 多項式時間演算法(polynomial time algorithm) 多項式演算法(polynomial algorithm) 範例: 費伯納西數列演算法

指數時間演算法 若一個演算法的時間複雜度為指數函數,則其為 範例: 遞迴費伯納西數列演算法 指數時間複雜度演算法(exponential time-complexity algorithm) 指數時間演算法(exponential time algorithm) 指數演算法(exponential algorithm) 範例: 遞迴費伯納西數列演算法

偽多項式時間演算法 若一個演算法的時間複雜度為輸入數值(numeric value)的多項式函數,但是卻是輸入長度(用以表達輸入的位元數)的指數函數,則其為 偽多項式時間複雜度演算法(pseudo-polynomial time-complexity algorithm) 偽多項式時間演算法(pseudo-polynomial time algorithm) 偽多項式演算法(pseudo-polynomial algorithm) 範例: PrimeCheck1 and PrimeCheck2演算法

費伯納西數列 費伯納西數(Fibonacci series)定義: F1 = F2 = 1 Fn = Fn-1 + Fn-2 1, 1, 2, 3, 5, 8, 13, 21, 34, …

費伯納西數列演算法 Algorithm Fibonacci(n) Input: 正整數n Output: 費伯納西數列第n項 if (n=1 or n=2) then return 1 else a1;b1 for i3 to n do ca+b ab bc end for return c end if Time Complexity: O(n)

遞迴費伯納西數列演算法 Algorithm RecursiveFibonacci(n) Input: 正整數n Output: 費伯納西數列第n項 if (n=1 or n=2) then return 1 else a  RecursiveFibonacci(n-1) b  RecursiveFibonacci(n-2) return a+b end if Time Complexity: O(2n)

遞迴費伯納西數列演算法 時間複雜度分析 假設遞迴費伯納西數列演算法的時間複雜度為T(n),則我們有: T(1)=T(2)=1 T(n)=T(n-1)+T(n-2)+12T(n-1)+1 =2(2T(n-2)+1)+1 =4(2T(n-3)+1)+1+2=… =2kT(n-k)+(1+2+…+2k-1)= 2kT(n-k)+(2k-1) 令n-k=1,則代入k=n-1,我們可得 T(n)  2n-1+(2n-1-1)  2n for n3 因此,T(n) = O(2n)

1.11 演算法複雜度分析範例

演算法複雜度分析範例 在本單元中,我們以氣泡排序(bubble sort)與插入排序(insertion sort)演算法為例,再一次演示演算法的複雜度分析。 所謂排序(sorting)是將一系列的元素(資料)依照某種順序排列的程序。例如,若元素為數值,則排序將依元素數值的大小以由小而大或由大而小的數值順序(numerical order)排列;又例如,若元素為字串,則排序將依字串的字典順序(lexical order)排列。 在本單元中,我們針對一個元素為數值的陣列,說明氣泡排序與插入排序演算法如何將陣列元素依由小而大的數值順序排列。然後我們針對這二個演算法的時間複雜度、空間複雜度進行分析與比較。

氣泡排序演算法 氣泡排序(bubble sort)演算法又稱為下沉(sinking)排序演算法或交換(exchange)排序演算法。 因為它的執行步驟中每回合(pass)會找出目前未處理過資料中最大的一個,就如同讓最大的氣泡先由水中冒出,然後再讓第二大的氣泡冒出,...,因此稱為氣泡排序演算法。 因為它在執行步驟中,如同每回合會讓最重的東西往下沉,然後再讓第二重的東西往下沉,...,因此稱為下沉排序演算法。 因為它的執行步驟為不斷的比較並交換兩個相鄰的資料,因此又稱為交換排序演算法。

氣泡排序演算法(續) 假設我們現在要使用氣泡排序演算法來將陣列中的n 個元素或資料(索引為0,...,n − 1)依照其值以由小而大的次序排列 其做法為持續兩兩比較相鄰資料,若前一筆(左邊)資料的值大於後一筆(右邊) 資料的值,則將此二筆資料互相交換,否則資料的位置即維持不動。上述的比較及交換過程一直持續進行到最後一筆資料被比對到為止,這稱為一個回合(pass)。 第一個回合的比對進行到最後一筆資料為止(n − 1 次比對),此時具最大值的資料已經調換至最後(最右邊) 的位置了;而第二回合的比對則進行到倒數第二筆資料為止,此時具第二大值的資料已經調換至倒數第二個位置了; … ;其餘依此類推。當進行完第n − 1 個回合(比較索引為0 與1 的資料) 之後,則所有的資料均已依由小到大的次序排列好了。

氣泡排序演算法(續)

氣泡排序演算法(續) 我們舉以下的實例來看看氣泡排序的運作情形: 假設有一個陣列具有 5 個元素 8、5、8’、6、7,索引為 0,...,4,其中 8 與 8’二個元素的值都是 8,但是為了區別起見,我們將之標示為 8 與 8’。 此陣列經由氣泡排序法排序的過程如下頁圖所示。其中加垂直線的方格代表索引變數 i 所指的元素,而加水平線的方格代表索引變數 j 所指的元素。

氣泡排序演算法(續)

氣泡排序演算法(續) 在整個氣泡排序演算法的過程中,8與8’的相對位置一直保持不變,也就是8一直排列在8’之前。 當一個排序演算法能夠讓具有相同值的元素維持原來的相對位置時,我們稱這種排序演算法為穩定(stable)排序演算法。 若我們所排序的元素只是一個資料庫的鍵值(key),則穩定排序演算法在排序之後可以保持所有紀錄原來的次序,這是非常有用的功能。 例如,我們可以先針對學生成績資料庫先依國文成績由高到低排序,之後再依總成績由高到低排序。此時資料庫資料的排列次序為總分高的在前,而總分相同的則由國文成績高的排在前面。

氣泡排序演算法(續) 除了幾個索引變數之外,氣泡排序演算法不需要額外的記憶體空間來輔助排序的進行。 不需要額外記憶體空間的排序演算法稱為就地(in place) 演算法。 這也是很好的特定,這表示氣泡排序演算法的空間複雜度可以視為是常數的(也就是O(1)),而與所需要排序的陣列的大小無關。

氣泡排序演算法(續)  

插入排序演算法 以下我們介紹插入排序(insertion sort)演算法。 假設我們想要使用插入排序演算法將一個陣列中的元素依照其值由小而大排列,其做法是從第二個元素開始,先以其為處理對象,並向前比對每一個元素以找出其適當插入位置;然後以第三個元素為處理對象,並向前比對每一個元素以找出其適當插入位置,...。 這類似我們在交考卷時的一種排序概念:每位學生寫完考卷時就交上自己的考卷;而老師為了方便讓考卷依座號的次序由小到大排序,在每個學生交上考卷時即在已經交上的考卷中從頭到尾(或從尾到頭)找出新交考卷的適當位置插入。 在老師手上的考卷永遠都是依座號的次序由小到大排列,而當所有學生都交出考卷時,所有的考卷就已經依座號的次序由小到大排序完畢了。

插入排序演算法(續) 以下為插入排序(insertion sort)演算法。

插入排序演算法(續) 我們舉以下的實例來看插入排序演算法的運作過程: 假設有一個陣列具有5個元素8、5、8’、6、7,索引(index)為0,...,4,其中8與8’二個元素的值都是8,但是為了區別起見,我們將之標示為8與8’。 此陣列經由插入排序演算法排序的過程如下頁之圖所示

插入排序演算法(續)

插入排序演算法(續) 在整個插入排序演算法的執行過程中,8 與 8’的相對位置一直保持不變,因此插入排序演算法也是一個穩定(stable)排序演算法。 除了幾個索引變數與一個陣列數值臨時儲存變數之外,插入排序演算法不需要額外的記憶體空間來輔助排序的進行,因此插入排序演算法也是一個就地 (in place) 演算法,其空間複雜度為 O(1)。

插入排序演算法(續)  

插入排序演算法(續) 資料已經由小到大排好的情況下為最佳狀況,在這種情況下,所有的mi=0 (1 ≤ i ≤ n−1),因此我們有M = 2(n−1) = 2n−2 =O(n)。 當資料為反向的由大到小排列時為最差狀況,在這種情況下,m1= 1;m2= 2; …;mn-1 = n − 1,或者我們寫為mi = i (1 ≤ i ≤ n − 1),因此我們有M = n(n-1)/2+2(n − 1)=O(n2)。

插入排序演算法(續)  

氣泡排序與插入排序演算法比較

The End

補充 漸近線 (asymptote): a straight line associated with a curve such that as a point moves along an infinite branch of the curve the distance from the point to the line approaches zero and the slope of the curve at the point approaches the slope of the line

定義大O記號 以下我們正式定義大O記號: [定義] 大O記號 (Big-O notation) 令f(n)與g(n)是由非負整數對應至非負實數的函數,若存在正實數常數c>0和正整數常數n0使得對所有的nn0而言,f(n)cg(n)成立,則我們說f(n)=O(g(n))。(唸作 「f(n)是屬於Big-O of g(n)」,比較正式的英文唸法為「f of n is of Big-O of g of n」)。 例如,針對35n2+12n+11而言,存在c=58和n0=1(58由35+12+11求得),使得當nn0=1時,35n2+12n+11cn2=(58n2)成立,因此,我們說35n2+12n+11=O(n2)。

漸近上界 (Asymptotic Upper Bound)

漸近下界 (Asymptotic Lower Bound )

漸近緊界 (Asymptotic Tight Bound ) f(n) = (g(n))

漸近上界記號: Big O Def: f(n) = O(g(n)) “上限(upper bound)" iff  c, n0  |f(n)|  c|g(n)|  n  n0 e.g. f(n) = 3n2 + 2 g(n) = n2  n0=2, c=4  f(n) = O(n2) e.g. f(n) = n3 + n = O(n3) e.g. f(n) = 3n2 + 2 = O(n3) or O(n100 )

漸近下界記號: Big Omega Def : f(n) = (g(n)) “下限(lower bound)” iff  c, and n0,  |f(n)|  c|g(n)|  n  n0 e.g. f(n) = 3n2 + 2 = (n2) or  (n)

漸近緊界記號: Theta Def : f(n) = (g(n)) iff  c1, c2, and n0,  c1|g(n)|  |f(n)|  c2|g(n)|  n  n0 e.g. f(n) = 3n2 + 2 =  (n2)

漸近緊界定理 對任意兩函數f(n) 與 g(n), ⇔ &