二分法和牛頓迭代法

2022-05-30 03:03:09 字數 3620 閱讀 7153

先說乙個面試題:問 1.2 - 0.2  ==  1 ?

答案是false! 為什麼?

其原因在於十進位制和二進位制的轉換上,計算機先要把十進位制的數轉化為二進位制,然後再計算。

但是,在轉化中,浮點數轉化為二進位制,就出問題了,例如:

十進位制的 0.1,轉化為二進位制是:0.0001100110011001100110011001100110011001100110011…(不能精確)

也就是說,轉化為二進位制後,不會精確等於十進位制的 0.1。

同時,計算機儲存的位數是有限制的,所以,就出現上述現象了。

這種問題不僅僅是 python 中有,所有支援浮點數運算的程式語言都會遇到,它不是 python 的 bug 

求乙個數的平方根函式sqrt(int num),在大多數語言中都提供實現。那麼要求乙個數的平方根,是怎麼實現的哪?

實際上求平方根的演算法方法主要有兩種:二分法( binary search)和牛頓迭代法( newton iteration):

說到這兩種方法,心裡就感慨重重,記得大學開了一門叫《計算方法》的課程,後來就忘了,直到有一天老師講到

這裡的時候才想起這種計算方法自己曾幾何時學過!

1、二分法:(夾逼法)

以求根號5為例:

a:折半:       5/2=2.5

b:平方校驗:  2.5*2.5=6.25>5,並且得到當前上限2.5

c:再次向下折半:2.5/2=1.25

d:平方校驗:1.25*1.25=1.5625<5,得到當前下限1.25

e:再次折半:2.5-(2.5-1.25)/2=1.875

f:平方校驗:1.875*1.875=3.515625<5,得到當前下限1.875

每次得到當前和5進行比較,並且記下下限和上線,依次迭代,逐漸逼近我們設好的誤差範圍:

import math

from

math import sqrt

def sqrt_binary(num):

x=sqrt(num) # 求出系統給我們的準確值

y=num/2.0    #中分

low=0.0

up=num*1.0

count=1

while abs(y-x)>0.00000001

: print count,y

count+=1

if (y*y>num):

up=y #二分後中間值比預期大了

y=low+(y-low)/2

else

: low=y

y=up-(up-y)/2 #二分後中間值比預期小了

returny

print(sqrt_binary(5))

print(sqrt(

5))

執行結果:

1 2.5

2 1.25

3 1.875

4 2.1875

5 2.34375

6 2.265625

7 2.2265625

8 2.24609375

9 2.236328125

10 2.2314453125

11 2.23388671875

12 2.23510742188

13 2.23571777344

14 2.23602294922

15 2.23617553711

16 2.23609924316

17 2.23606109619

18 2.23608016968

19 2.23607063293

20 2.23606586456

21 2.23606824875

22 2.23606705666

23 2.2360676527

24 2.23606795073

25 2.23606809974

26 2.23606802523

27 2.23606798798

2.23606796935

2.2360679775

經過27次二分迭代,得到的值和系統sqrt()差別在0.0000001,

2、牛頓迭代法:

仔細思考一下就能發現,我們需要解決的問題可以簡單化理解。

從函式意義上理解:我們是要求函式f(x) = x²,使f(x) = num的近似解,即x² - num = 0的近似解。

從幾何意義上理解:我們是要求拋物線g(x) = x² - num與x軸交點(g(x) = 0)最接近的點。

我們假設g(x0)=0,即x0是正解,那麼我們要做的就是讓近似解x不斷逼近x0,這是函式導數的定義

可以由此得到: (可以寫出f'(x)的導數形式)   

從幾何圖形上看,因為導數是切線,通過不斷迭代,導數與x軸的交點會不斷逼近x0。

所以對於一般情況:

將m=2帶入得:

**如下:

def sqrt_newton(num):

x=sqrt(num)

y=num/2.0

count=1

while abs(y-x)>0.00000001

: print count,y

count+=1

y=((y*1.0)+(1.0*num)/y)/2.0000

returny

print(sqrt_newton(5))

print(sqrt(

5))

運算結果: 

1 2.5

2 2.25

3 2.23611111111

對於牛頓迭代法來說:還可以求立方,甚至等多(只需要修改m得值即可)

def cube_newton(num):

x=num/3.0

y=0count=1

while abs(x-y)>0.00000001

: print count,x

count+=1

y=x x=(2.0/3.0)*x+(num*1.0)/(x*x*3.0

)

returnx

print(cube_newton(

27))

牛頓迭代法 二分法

牛頓迭代法 用牛頓迭代法求f x 0在x0附近的乙個實根的方法是 1 選乙個接近於x的真實根的近似根x1 2 通過x1求出f x1 在幾何上就是作x x1,交f x 於f x1 3 過f x1 作f x 的切線,交x軸於x2。可以用公式求出x2。由於f x1 f x1 x2 x1 故x2 x1 f ...

牛頓迭代法和二分法求方程根

0x03直接給出公式x x0 f x0 f x0 設迭代到 x x0 1e 5例子 2x 3 4 x 3x 6 0 include include int main while fabs x x0 1e 5 return x 0x04簡單說一下 f就是式子,fd為f x 然後套用公式就好了最後判斷是...

sqrt函式實現 二分法 牛頓迭代法

在leetcode練習時,碰到一道經典的面試題,如何實現sqrt 開平方函式。當然,很簡單的是呼叫系統函式,但是難道不能自己實現這個函式的功能嗎?於是一番思索和查閱資料,看到下面的方法。二分法這個應該很熟悉,在二分查詢演算法中就有具體的體現。應用在此題上,也是合適不過的。首先分析一下這道題 實現sq...