算法初步
0. 從1加到100?
先寫一個從1加到100的程序,如何寫呢?
記剛學程序時的寫法:
int i, sum = 0, n = 100;
for(i = 0; i < 100; i++)
{
sum += i;
}
printf("%d", sum);
大神高斯小學時的解法用程序實現:
int sum = 0, n = 100;
sum = (1 + n) * n / 2;
printf("%d", sum);
我們看到的這兩種解決從1加到100的解決方法,就是算法。
1. 算法定義
算法是解決特定問題求解步驟的描述,在計算機中表現為指令的有限序列,並且每條指令表示一個或者多個操作。
2. 算法的特性
- 輸入輸出:算法具有零個或者多個輸入。對於多數算法來說,輸入參數是必要的,但是對於打印“hello world!”這樣的代碼,就不需要參數。算法至少有一個或多個輸出。倘若一個算法沒有輸出,那我們要它何用?當然輸出方式可以是打印輸出或者返回一個或多個值等。
- 有窮性:指算法在執行有限步驟後,自動結束而不是無限循環,並且每一個步驟在可以接受的時間內執行完成。這裏的有窮也是有邊界的,並不是說執行一個算法需要二三十年也是能完成的,那樣黃瓜菜都涼了,意義不大了。
- 確定性:算法的每一個步驟都有確定意義的,不會出現二義性。
- 可行性:算法的每一步驟都必須是可行的,也就是說,每一個步驟都能執行有限次數後完成。
3. 算法效率的度量方法
3.1 事後統計法
通過設計好的測試程序和數據,利用計算機計時器對不同的算法編制的程序的運行時間進行比較,從而確定算法的效率高低。
3.2 事前分析估算法
在算法編制前,根據統計方法對算法進行估算。
算法效率的度量也就是對算法運行時間的度量,而一個程序的運行時間,主要依賴於算法的好壞和輸入規模。
輸入規模即輸入量的多少。
看栗子:
第一種求和算法:
int i, sum = 0, n = 100; //執行1次 for(i = 0; i < 100; i++) //執行年n+1次 { sum += i; //執行n次 } printf("%d", sum); //執行1次
第二種求和算法:
int sum = 0, n = 100; //執行1次
sum = (1 + n) * n / 2; //執行1次
printf("%d", sum); //執行1次
顯然,第一種算法執行了2n+3次,第二種算法執行了3次,兩種算法的好壞顯而易見。
4. 函數的漸進增長
舉個例子:
相同的輸入規模n,算法A做2n+3次操作,算法B做3n+1次操作。兩個算法誰更快呢?
顯然,答案不確定,當n=1時,算法B快些;當n=2時,兩者相等;當n>2時,算法A快。
此時給出這樣的定義,輸入規模n在沒有限制的情況下,只要超過一個數值N,這個函數就總是大於另一個函數,我們就稱函數是漸進增長的。
函數的漸進增長:給定兩個函數f(n)和g(n),如果存在一個整數N,
使得對於所有的n>N,f(n)總是比g(n)大,那麽,我們說f(n)的增長漸進快於g(n)。
根據函數的漸進增長性我們可以得出:某個算法,隨著n的增大,它會越來越優於另一個算法,或者越來越差於另一個算法。
5. 算法的時間復雜度
5.1算法時間復雜度定義
在進行算法分析時,語句總的執行次數T(n)是關於輸入規模n的函數,進而分析T(n)隨n的變化情況並確定T(n)的數量級。算法的時間復雜度,也就是算法的時間度量,記作:T(n) = O(f(n))。它表示隨問題規模n的增大,算法執行時間的增長率和f(n)的增長率相同,稱作算法的漸進時間復雜度,簡稱時間復雜度。其中f(n)是問題規模n的某個函數。
5.2 大O階的推導
大O階的推導:
1.用常數1取代運行時間中的所有加法常數。
2.在修改後的運行次數函數中,只保留最高階項。
3.如果最高階項存在且不是1,則去除與這個項相乘的常數。
得到的結果就是大O階。
5.3 常數階O(1)
int sum = 0, n = 100; //執行1次
sum = (1 + n) * n / 2; //執行1次
printf("%d", sum); //執行1次
5.4 線性階O(n)
int i;
for(i = 0; i < n; i++)
{
//時間復雜度為O(1)的程序步驟序列
}
5.5 對數階O(logn)
int count = 1;
while(count < n)
{
count = count * 2;
//時間復雜度為O(1)的程序步驟序列
}
5.6 平方階O(n2)
int i, j;
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
//時間復雜度為O(1)的程序步驟序列
}
}
計算如下程序的時間復雜度:
int i, j;
for(i = 0; i < n; i++)
{
for(j = i; j < n; j++)
{
//時間復雜度為O(1)的程序步驟序列
}
}
當i=1時,內循環執行了n-1次;當i=n-1時,內循環執行了1次,我們可以推算出總的執行次數為:
n+(n-1)+(n-2)+(n-3)+……+1=n(n+1)/2=n2/2+n/2
用大O推導法,可得這段代碼的時間復雜度為O(n2)
5.7 常見時間復雜度
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2?)<O(n!)
?
算法初步