第四百一十四節,python經常使用算法學習

本節內容html

  1. 算法定義
  2. 時間複雜度
  3. 空間複雜度
  4. 經常使用算法實例

1.算法定義 

算法(Algorithm)是指解題方案的準確而完整的描述,是一系列解決問題的清晰指令,算法表明着用系統的方法描述解決問題的策略機制。也就是說,可以對必定規範的輸入,在有限時間內得到所要求的輸出。若是一個算法有缺陷,或不適合於某個問題,執行這個算法將不會解決這個問題。不一樣的算法可能用不一樣的時間、空間或效率來完成一樣的任務。一個算法的優劣能夠用空間複雜度與時間複雜度來衡量。算法

一個算法應該具備如下七個重要的特徵:數組

①有窮性(Finiteness):算法的有窮性是指算法必須能在執行有限個步驟以後終止;函數

②確切性(Definiteness):算法的每一步驟必須有確切的定義;測試

③輸入項(Input):一個算法有0個或多個輸入,以刻畫運算對象的初始狀況,所謂0個輸     入是指算法自己定出了初始條件;網站

④輸出項(Output):一個算法有一個或多個輸出,以反映對輸入數據加工後的結果。沒       有輸出的算法是毫無心義的;spa

⑤可行性(Effectiveness):算法中執行的任何計算步驟都是能夠被分解爲基本的可執行       的操做步,即每一個計算步均可以在有限時間內完成(也稱之爲有效性);code

⑥高效性(High efficiency):執行速度快,佔用資源少;htm

⑦健壯性(Robustness):對數據響應正確。對象

 

叫賣錄音網
錄音網站

 

2. 時間複雜度

計算機科學中,算法的時間複雜度是一個函數,它定量描述了該算法的運行時間,時間複雜度經常使用大O符號(大O符號(Big O notation)是用於描述函數漸進行爲的數學符號。更確切地說,它是用另外一個(一般更簡單的)函數來描述一個函數數量級的漸近上界。在數學中,它通常用來刻畫被截斷的無窮級數尤爲是漸近級數的剩餘項;在計算機科學中,它在分析算法複雜性的方面很是有用。)表述,使用這種方式時,時間複雜度可被稱爲是漸近的,它考察當輸入值大小趨近無窮時的狀況。

大O,簡而言之能夠認爲它的含義是「order of」(大約是)

無窮大漸近
大O符號在分析算法效率的時候很是有用。舉個例子,解決一個規模爲 n 的問題所花費的時間(或者所需步驟的數目)能夠被求得:T(n) = 4n^2 - 2n + 2。
當 n 增大時,n^2; 項將開始占主導地位,而其餘各項能夠被忽略——舉例說明:當 n = 500,4n^2; 項是 2n 項的1000倍大,所以在大多數場合下,省略後者對錶達式的值的影響將是能夠忽略不計的。

 

常數階O(1)

常數又稱定數,是指一個數值不變的常量,與之相反的是變量

爲何下面算法的時間複雜度不是O(3),而是O(1)。

1
2
3
int sum = 0,n = 100; /*執行一次*/ 
sum = (1+n)*n/2; /*執行一次*/ 
printf("%d", sum); /*行次*/

這個算法的運行次數函數是f(n)=3。根據咱們推導大O階的方法,第一步就是把常數項3改成1。在保留最高階項時發現,它根本沒有最高階項,因此這個算法的時間複雜度爲O(1)。

另外,咱們試想一下,若是這個算法當中的語句sum=(1+n)*n/2有10句,即:

1
2
3
4
5
6
7
8
9
10
11
12
int sum = 0, n = 100; /*執行1次*/ 
sum = (1+n)*n/2; /*執行第1次*/ 
sum = (1+n)*n/2; /*執行第2次*/ 
sum = (1+n)*n/2; /*執行第3次*/ 
sum = (1+n)*n/2; /*執行第4次*/ 
sum = (1+n)*n/2; /*執行第5次*/ 
sum = (1+n)*n/2; /*執行第6次*/ 
sum = (1+n)*n/2; /*執行第7次*/ 
sum = (1+n)*n/2; /*執行第8次*/ 
sum = (1+n)*n/2; /*執行第9次*/ 
sum = (1+n)*n/2; /*執行第10次*/ 
printf("%d",sum); /*執行1次*/

事實上不管n爲多少,上面的兩段代碼就是3次和12次執行的差別。這種與問題的大小無關(n的多少),執行時間恆定的算法,咱們稱之爲具備O(1)的時間複雜度,又叫常數階。

注意:無論這個常數是多少,咱們都記做O(1),而不能是O(3)、O(12)等其餘任何數字,這是初學者經常犯的錯誤。

 

 O(n【時間規模,也能夠理解爲運行次數】),當運算裏沒有最高階項(也就是次方)時,時間規模就是1,因此爲o(1),

若是運算裏包含了最高階項(也就是次方)時,且次方數不爲1時,時間規模就是最高階項(也就是次方數),如o(3),

 

推導大O階方法

1.用常數1取代運行時間中的全部加法常數

2.在修改後的運行次數函數中,只保留最高階項

3.若是最高階項存在且不是1,則去除與這個項相乘的常數

  

對數階O(log2n) 

對數

若是a的x次方等於N(a>0,且a不等於1),那麼數x叫作以a爲底N的對數(logarithm),記做x=logaN, 。其中,a叫作對數的底數,N叫作真數。
5^2 = 25 , 記做 2= log5 25 
對數是一種運算,與指數是互逆的運算。例如

① 3^2=9 <==> 2=log<3>9;

② 4^(3/2)=8 <==> 3/2=log<4>8;

③ 10^n=35 <==> n=lg35。爲了使用方便,人們逐漸把以10爲底的經常使用對數記做lgN

 

對數階

1
2
3
4
5
6
7
8
9
int count = 1;
 
while (count < n)
 
{   
 
count = count * 2; /* 時間複雜度爲O(1)的程序步驟序列 */
 
}


因爲每次count乘以2以後,就距離n更近了一分。

也就是說,有多少個2相乘後大於n,則會退出循環。

由2^x=n獲得x=log2n。因此這個循環的時間複雜度爲O(logn)。

 

線性階O(n)  

執行時間隨問題規模增加呈正比例增加

1
2
3
4
5
data = [ 8,3,67,77,78,22,6,3,88,21,2]
find_num = 22
for i in data:
     if i == 22:
         print("find",find_num,i )

 

線性對數階O(nlog2n)

 

 

平方階O(n^2)

1
2
3
4
for i in range(100):
 
     for k in range(100):
         print(i,k)

  

立方階O(n^3)
k次方階O(n^k),
指數階O(2^n)。
隨着問題規模n的不斷增大,上述時間複雜度不斷增大,算法的執行效率越低。  

 

 

1、計算方法
1.一個算法執行所耗費的時間,從理論上是不能算出來的,必須上機運行測試才能知道。但咱們不可能也沒有必要對每一個算法都上機測試,只需知道哪一個算法花費的時間多,哪一個算法花費的時間少就能夠了。而且一個算法花費的時間與算法中語句的執行次數成正比例,哪一個算法中語句執行次數多,它花費時間就多。
一個算法中的語句執行次數稱爲語句頻度或時間頻度。記爲T(n)。
2.通常狀況下,算法的基本操做重複執行的次數是模塊n的某一個函數f(n),所以,算法的時間複雜度記作:T(n)=O(f(n))。隨着模塊n的增大,算法執行的時間的增加率和f(n)的增加率成正比,因此f(n)越小,算法的時間複雜度越低,算法的效率越高。
在計算時間複雜度的時候,先找出算法的基本操做,而後根據相應的各語句肯定它的執行次數,再找出T(n)的同數量級(它的同數量級有如下:1,Log2n ,n ,nLog2n ,n的平方,n的三次方,2的n次方,n!),找出後,f(n)=該數量級,若T(n)/f(n)求極限可獲得一常數c,則時間複雜度T(n)=O(f(n))。
3.常見的時間複雜度
按數量級遞增排列,常見的時間複雜度有:
常數階O(1),  對數階O(log2n),  線性階O(n),  線性對數階O(nlog2n),  平方階O(n^2), 立方階O(n^3),..., k次方階O(n^k), 指數階O(2^n) 。
其中,
1.O(n),O(n^2), 立方階O(n^3),..., k次方階O(n^k) 爲多項式階時間複雜度,分別稱爲一階時間複雜度,二階時間複雜度。。。。
2.O(2^n),指數階時間複雜度,該種不實用
3.對數階O(log2n),   線性對數階O(nlog2n),除了常數階之外,該種效率最高
例:算法:
  for(i=1;i<=n;++i)
  {
     for(j=1;j<=n;++j)
     {
         c[ i ][ j ]=0; //該步驟屬於基本操做 執行次數:n^2
          for(k=1;k<=n;++k)
               c[ i ][ j ]+=a[ i ][ k ]*b[ k ][ j ]; //該步驟屬於基本操做 執行次數:n^3
     }
  }
  則有 T(n)= n^2+n^3,根據上面括號裏的同數量級,咱們能夠肯定 n^3爲T(n)的同數量級
  則有f(n)= n^3,而後根據T(n)/f(n)求極限可獲得常數c
  則該算法的 時間複雜度:T(n)=O(n^3)
4、

 

定義:若是一個問題的規模是n,解這一問題的某一算法所須要的時間爲T(n),它是n的某一函數 T(n)稱爲這一算法的「時間複雜性」。

當輸入量n逐漸加大時,時間複雜性的極限情形稱爲算法的「漸近時間複雜性」。

咱們經常使用大O表示法表示時間複雜性,注意它是某一個算法的時間複雜性。大O表示只是說有上界,由定義若是f(n)=O(n),那顯然成立f(n)=O(n^2),它給你一個上界,但並非上確界,但人們在表示的時候通常都習慣表示前者。

此外,一個問題自己也有它的複雜性,若是某個算法的複雜性到達了這個問題複雜性的下界,那就稱這樣的算法是最佳算法。

「大O記法」:在這種描述中使用的基本參數是 n,即問題實例的規模,把複雜性或運行時間表達爲n的函數。這裏的「O」表示量級 (order),好比說「二分檢索是 O(logn)的」,也就是說它須要「經過logn量級的步驟去檢索一個規模爲n的數組」記法 O ( f(n) )表示當 n增大時,運行時間至多將以正比於 f(n)的速度增加。

這種漸進估計對算法的理論分析和大體比較是很是有價值的,但在實踐中細節也可能形成差別。例如,一個低附加代價的O(n2)算法在n較小的狀況下可能比一個高附加代價的 O(nlogn)算法運行得更快。固然,隨着n足夠大之後,具備較慢上升函數的算法必然工做得更快。

O(1)

Temp=i;i=j;j=temp;                    

以上三條單個語句的頻度均爲1,該程序段的執行時間是一個與問題規模n無關的常數。算法的時間複雜度爲常數階,記做T(n)=O(1)。若是算法的執行時間不隨着問題規模n的增長而增加,即便算法中有上千條語句,其執行時間也不過是一個較大的常數。此類算法的時間複雜度是O(1)。

O(n^2)

2.1. 交換i和j的內容
     sum=0;                 (一次)
     for(i=1;i<=n;i++)       (n次 )
        for(j=1;j<=n;j++) (n^2次 )
         sum++;       (n^2次 )
解:T(n)=2n^2+n+1 =O(n^2)

2.2.   
    for (i=1;i<n;i++)
    {
        y=y+1;         ①   
        for (j=0;j<=(2*n);j++)    
           x++;        ②      
    }         
解: 語句1的頻度是n-1
          語句2的頻度是(n-1)*(2n+1)=2n^2-n-1
          f(n)=2n^2-n-1+(n-1)=2n^2-2
          該程序的時間複雜度T(n)=O(n^2).         

O(n)      
                                                      
2.3.
    a=0;
    b=1;                      ①
    for (i=1;i<=n;i++) ②
    {  
       s=a+b;    ③
       b=a;     ④  
       a=s;     ⑤
    }
解:語句1的頻度:2,        
           語句2的頻度: n,        
          語句3的頻度: n-1,        
          語句4的頻度:n-1,    
          語句5的頻度:n-1,                                  
          T(n)=2+n+3(n-1)=4n-1=O(n).
                                                                                                 
O(log2n )2.4.     i=1;       ①    while (i<=n)       i=i*2; ②解: 語句1的頻度是1,            設語句2的頻度是f(n),   則:2^f(n)<=n;f(n)<=log2n              取最大值f(n)= log2n,          T(n)=O(log2n )O(n^3)2.5.    for(i=0;i<n;i++)    {         for(j=0;j<i;j++)         {          for(k=0;k<j;k++)             x=x+2;         }    }解:當i=m, j=k的時候,內層循環的次數爲k當i=m時, j 能夠取 0,1,...,m-1 , 因此這裏最內循環共進行了0+1+...+m-1=(m-1)m/2次因此,i從0取到n, 則循環共進行了: 0+(1-1)*1/2+...+(n-1)n/2=n(n+1)(n-1)/6因此時間複雜度爲O(n^3).                                  咱們還應該區分算法的最壞狀況的行爲和指望行爲。如快速排序的最 壞狀況運行時間是 O(n^2),但指望時間是 O(nlogn)。經過每次都仔細 地選擇基準值,咱們有可能把平方狀況 (即O(n^2)狀況)的機率減少到幾乎等於 0。在實際中,精心實現的快速排序通常都能以 (O(nlogn)時間運行。下面是一些經常使用的記法:訪問數組中的元素是常數時間操做,或說O(1)操做。一個算法如 果能在每一個步驟去掉一半數據元素,如二分檢索,一般它就取 O(logn)時間。用strcmp比較兩個具備n個字符的串須要O(n)時間。常規的矩陣乘算法是O(n^3),由於算出每一個元素都須要將n對 元素相乘並加到一塊兒,全部元素的個數是n^2。指數時間算法一般來源於須要求出全部可能結果。例如,n個元 素的集合共有2n個子集,因此要求出全部子集的算法將是O(2n)的。指數算法通常說來是太複雜了,除非n的值很是小,由於,在 這個問題中增長一個元素就致使運行時間加倍。不幸的是,確實有許多問題 (如著名的「巡迴售貨員問題」 ),到目前爲止找到的算法都是指數的。若是咱們真的遇到這種狀況,一般應該用尋找近似最佳結果的算法替代之。
相關文章
相關標籤/搜索