算法的定義是完成一項任務的一系列步驟,就像一份食譜,第一步幹什麼,第二步幹什麼... 在計算機科學中,算法是完成一個任務的一系列步驟,對於完成一個任務,有好的算法也有壞的算法,找到一個優秀的算法可讓任務高效的完成。一個好的算法要知足兩點正確性和高效,可是有時候也不要去徹底正確足夠好就行,好比一項任務要獲得一個徹底正確結果須要很是長的時間。python
給一個數怎麼找到它的立方根呢?咱們知道沒法找到隨便一個數的精確立方根,因此咱們能夠接受必定偏差。算法
咱們可讓從0開始不斷的增長它的大小,看它的三次方有多接近,找出最接近的數。數組
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
複製代碼
能夠猜到當很大時,這個算法須要的時間就很是長。那麼有什麼更好的算法?函數
二分搜索算法(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的平方根,首先找到一個多項式知足,並對它求導獲得,牛頓法告訴咱們若是一個數很接近它的根,那麼就更加接近它的根。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
複製代碼
能夠看到代碼很緊湊,可是很是快比二分搜索算法還要快!數學
上面的方法都解決同一個問題,可是速度有快有慢,那麼咱們怎麼描述一個算法的快慢?it
大O符號(Big O notation),又稱爲漸進符號,是用於描述函數漸近行爲的數學符號。更確切地說,它是用另外一個(一般更簡單的)函數來描述一個函數數量級的漸近上界。在數學中,它通常用來刻畫被截斷的無窮級數尤爲是漸近級數的剩餘項;在計算機科學中,它在分析算法複雜性的方面很是有用。
大O符號描述一個算法在最壞狀況下的複雜度。
好比有個累加函數
def add(n):
ans = 0
while n > 0:
ans = ans + n
n = n - 1
return ans
複製代碼
能夠看到這個函數一共要執行步,可是大O表示法只關心當增大時占主導地位的項目,其餘項目和係數均可以忽略。這個函數用大O符號就爲是線性複雜度。
符號 | 名稱 |
---|---|
常數 | |
對數 | |
多對數 | |
線性 | |
線性對數 | |
多項式 | |
指數 | |
階乘 |
好比一個函數內有兩個不一樣複雜度的循環,
好比循環嵌套循環,
除了大O符號還有一些不經常使用的符號。
大符號(Big-Omega notation)的意思恰好和大O符號相反。大符號表示函數在增加到必定程度時總大於一個特定函數的常數倍。不提供上限,算法最少要花多少時間。
大符號(Big-Theta notation)是大O符號和大符號的結合。
好比一個算法最慢爲最快爲,那麼用大表示就爲,大和大O看起來差很少,可是它們表達的意思不同,是表示隨着的增大函數實際增加率不會超過,是表示隨着的增大就很是接近函數實際增加率。