算法基礎 I — 二分搜索算法、牛頓法

什麼是算法?

算法的定義是完成一項任務的一系列步驟,就像一份食譜,第一步幹什麼,第二步幹什麼... 在計算機科學中,算法是完成一個任務的一系列步驟,對於完成一個任務,有好的算法也有壞的算法,找到一個優秀的算法可讓任務高效的完成。一個好的算法要知足兩點正確性高效,可是有時候也不要去徹底正確足夠好就行,好比一項任務要獲得一個徹底正確結果須要很是長的時間。python

找到立方根

給一個數n怎麼找到它的立方根呢?咱們知道沒法找到隨便一個數的精確立方根,因此咱們能夠接受必定偏差。算法

咱們可讓x從0開始不斷的增長它的大小,看它的三次方有多接近n,找出最接近n的數。數組

def cuberoot(n):
    inc = 0.001 # 每次遞增的數,越小精度越大
    eps = 0.01 # 可接受的偏差範圍
    ans = 0.0
    while abs(ans ** 3 - n) >= eps and ans < abs(n):
        ans += inc
    if n < 0: ans *= -1
    return ans
複製代碼

能夠猜到當n很大時,這個算法須要的時間就很是長。那麼有什麼更好的算法?函數

二分搜索算法

二分搜索算法(binary search)也叫折半搜索,是一種在有序數組中查找某一特定元素的搜索算法。從數組的中間開始尋找,看是否是要找的數,若是不是就看這個數字是大於仍是小於要找的數,而後把不對的那一半扔掉。這個算法每次都搜索範圍縮小一半,因此是一個很是快的算法。spa

上面找到立方根問題用二分搜索算法解決就是這樣,code

def cuberoot(n):
    eps = 0.01
    low = 0.0 # 下界
    high = n # 上界
    ans = (low + high) / 2
    while abs(ans ** 3 - n) >= eps:
        if ans ** 3 < n:
            low = ans
        else:
            high = ans
        ans = (low + high) / 2
    if n < 0: ans *= -1
    return ans
複製代碼

對比原來的算法能夠看到,二分搜索算法快多了,原來到迭代幾千次,如今十幾回就好了!可是還有沒有更快的算法呢?cdn

牛頓法

牛頓法(Newton's method)又稱爲牛頓-拉弗森方法(Newton-Raphson method)。簡單來講牛頓法能夠快速的找到任何多項式的根(不光是立方根)。好比咱們要找到25的平方根,首先找到一個多項式p(x)=x^2-25知足p(r)=0,並對它求導獲得p'(x)=2x,牛頓法告訴咱們若是一個數g很接近它的根,那麼g-\frac{p(g)}{p'(g)}就更加接近它的根。blog

def cuberoot(n):
    eps = 0.01
    g = n / 3 # 隨便猜個數
    while abs(g ** 3 - n) >= eps:
        g = g - (g ** 3 - n) / (g ** 2 * 3)
    return g
複製代碼

能夠看到代碼很緊湊,可是很是快比二分搜索算法還要快!數學

大O符號

上面的方法都解決同一個問題,可是速度有快有慢,那麼咱們怎麼描述一個算法的快慢?it

  1. 咱們須要根據輸入大小肯定算法須要多長時間。
  2. 咱們必須知道函數隨輸入大小增加的速度。

大O符號(Big O notation),又稱爲漸進符號,是用於描述函數漸近行爲的數學符號。更確切地說,它是用另外一個(一般更簡單的)函數來描述一個函數數量級的漸近上界。在數學中,它通常用來刻畫被截斷的無窮級數尤爲是漸近級數的剩餘項;在計算機科學中,它在分析算法複雜性的方面很是有用。

大O符號描述一個算法在最壞狀況下的複雜度。

好比有個累加函數

def add(n):
    ans = 0
    while n > 0:
        ans = ans + n
        n = n - 1
    return ans
複製代碼

能夠看到這個函數一共要執行1+5n+1步,可是大O表示法只關心當n增大時占主導地位的項目,其餘項目和係數均可以忽略。這個函數用大O符號就爲O(n)是線性複雜度。

複雜度分類(從快到慢)
符號 名稱
O(1) 常數
O(log\ n) 對數
O((log\ n)^c) 多對數
O(n) 線性
O(n\ log\ n) 線性對數
O(n^c) 多項式
c^n 指數
n! 階乘
加法法則

O(f(n))+O(g(n))=O(f(n)+g(n))

好比一個函數內有兩個不一樣複雜度的循環,O(n) + O(n^2) = O(n^2+n) = O(n^2)

乘法法則

O(f(n))*O(g(n))=O(f(n)*g(n))

好比循環嵌套循環,O(n) * O(n)=O(n^2)

其餘表示符號

除了大O符號還有一些不經常使用的符號。

\Omega符號

\Omega符號(Big-Omega notation)的意思恰好和大O符號相反。大\Omega符號表示函數在增加到必定程度時總大於一個特定函數的常數倍。不提供上限,算法最少要花多少時間。

\Theta符號

\Theta符號(Big-Theta notation)是大O符號和大\Omega符號的結合。

好比一個算法最慢爲an^2+n+10最快爲bn^2+n+10,那麼用大\Theta表示就爲\Theta(n^2),大\Theta(n)和大O看起來差很少,可是它們表達的意思不同,O(f(n))是表示隨着n的增大函數實際增加率不會超過f(n)\Theta(f(n))是表示隨着n的增大f(n)就很是接近函數實際增加率。

相關文章
相關標籤/搜索