求整數的近似平方根(牛頓迭代法)

曾經面試遇到過一道編程題:

給出一個整數,求其近似平方根。

當時沒有想到更好的方法,就暴力進行了破解,這當然不會留下好的印象。其實,這道題使用牛頓迭代法可以十分高效的解決。

一、什麼是牛頓迭代法

假設有函數:f(x) = 0,要想求出其根,則可以:

  1. 給出一個初始點x0,則在該點的切線爲:L: y = f(x0) + {f^'}{(x0)} (x - x0)
  2. 沿着切線方向,與橫軸相交,也即令:f(x0) + {f^'}{(x0)} (x - x0) = 0,則求得:x = x0 - f(x0)/ {f^'}{(x0)}
  3. 更新x0,令:x0 = x
  4. 按照1~3步驟迭代下去,直到精度滿足要求;

上述算法的第1、2步,其實也就是函數f(x)x0處的泰勒展開取前兩項:

f(x) = f(x0) + {f^'}(x0)(x-x0) + {f^''}(x0){(x-x0)^2}/2! + ... + {f^{(n)}}(x0){(x-x0)^n}/n! +R_n(x)

上述泰勒展開式,取前兩項並使之等於0,則有:f(x0) + {f^'}(x0)(x-x0),同樣可以得到步驟2中的迭代公式。

 

牛頓迭代法示意

二、解決求根問題

對於求一個整數近似平方根這個問題,我們可以簡單做一個轉換,使得問題變爲一個方程:x^2 - n = 0。對於方程,n是已知待求平方根的整數,x爲我們的求解目標,此時,我們的目的就變成了求解f(x) = x^2-n=0的根了!

那麼,利用上述的牛頓迭代法,即可輕鬆解決。廢話少說,直接上碼:

def square_root(n:int)->float:
    """ f(x) = x^2 - n = 0,求該方程的根
    """
    x0 = 1
    i = 0
    while True:
        i += 1
        x1 = x0 - (x0**2 - n)/(2*x0)
        if x1 - x0 < 1e-10 and x1-x0> -1e-10:
            print("total iterations: ", i+1)
            break
        x0 = x1
    return x1

代碼中:

我們給定初始值爲1,這裏需要注意的是,我們給的初始值不能是方程的極值點,否則利用牛頓迭代法則無法繼續優化下去;

設定了迭代結束條件:|x-x0| < 10^{-10},當滿足該條件時,說明求解的精度已經很高了,此時的迭代結果即可作爲近似根了。

三、拓展一下

第二部分,給出了使用牛頓迭代法求解給定整數近似平方根的方法,我們同樣可以用於處理其他問題,如求解給定整數立方根..n次方根、給定任意方程,求其近似解等問題。

下面給出求解立方根的解法,與求解平方根十分相似,唯一不同之處就在於目標迭代公式稍微發生一點變化:

def cube_root(n:int)->float:
    """ f(x) = x^3 - n = 0,求該方程的根
    """
    x0 = 1
    i = 0
    while True:
        i += 1
        x1 = x0 - (x0**3 - n)/(3*x0**2)
        if x1 - x0 < 1e-20 and x1-x0> -1e-20:
            print("total iterations: ", i+1)
            break
        x0 = x1
    return x1

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章