平常我們用到的 sqrt 函數求一個數的算術平方根,以前一直好奇究竟是如何計算的。
這篇文章我們就一起來探究一下。
二分法以前我想到的一種方式是二分法;
假設求根号2的平方根;
假設最開始 min = 1.0,max = 2.0;
則它們的中間值 val = (min max)/2.0;
然後判斷 num = val*val 的結果,
如果 num > 2;則 max = val;
如果 num < 2;則 min = val;
如果 num = 2;則 算術平方根是 val,返回。
當然有人會問,一直不等于能,當然我們可以設置計算次數;
比如執行超過 20 次後就返回,這樣可以避免無線循環下去。
然而這種方法的收斂速度實在太慢,導緻要計算很多次才能達到比較高的精度。
牛頓的方法網上看到一個說是牛頓的計算方法,假設 f(x) = x^2-2;
在 x^2-2 的曲線上面,先找一個點A(X0,Y0),
過點A做曲線的切線交x軸于B(X1,0);
找到當前點B對應曲線上的點C(X1,Y1);
過點C做曲線的切線交x軸于D(X2,0);
找到當前點D對應曲線上的點E(X2,Y2);
過點E做曲線的切線交x軸于F(X3,0);
.........
按照這個過程一直下去,B D F....将會離曲線與x軸的交點越來越近,即逼近原理。
數學方法
那上面的坐标如何求取呢,對于點A,可以帶入一個方便的坐标(1,-1);
由于CD是切線,點C為切點,則有如下關系:
斜率 y' = BC/BD
而:BD 可以看成是點B的x軸坐标減去點D的x軸坐标,即 BD = X1-X2;
BC 就是C點的y值,即Y1;
上面關系就變成:y' = Y1/(X1-X2)
轉換一下:X1-X2 = Y1/y'
X2 = X1-Y1/y'
轉換成标準的寫法,則有: Xn = Xn-1 - f(Xn-1) / f '(Xn-1)
對于曲線 x^2-2 任意一點的切線可以根據多項式導數方式獲取,即 f '(Xn-1) = 2x;
則有 Xn = Xn-1 - f(Xn-1) / 2x;
将A點(X0,Y0) 由曲線上的點(1,-1)帶入時,
X1 = 1 - (-1/2*1) = 1.5; 此時 Y1 = 1.5^2-2 = 2.25-2 = 0.25;
X2 = 1.5 - 0.25/2*1.5 = 1.416666...667; 此時 Y1 = 0.0069444444...
以此類推
X6 = 1.4142135623730950488016887242096980785696718753772.......
對比網上查找到的根号2前100為如下:
1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850....
可以看到X6寫出來的,僅僅是最後兩位開始不一樣。可見運算次數僅僅6次,精度已經如此高了。
代碼實現由于C/C 沒找到比較穩定的高精度計算數據類,在此用Python代替了。
實現代碼如下:
from decimal import Decimal
from decimal import getcontext
work_context = getcontext()
work_context.prec = 1000 // 有興趣的可以試試更高精度
num = Decimal(2) // 需要開方的數,可以試試3,5,7,11 。。。
def Xn(x, y):
x -= y/(x*Decimal(2))
y = x*x-num
return (x,y)
x = Decimal(1)
y = x*x-num
for i in range(0,20): // 計算20次精度已經非常高了
x, y = Xn(x, y)
print(x)
第20次結果:(好像精度已經達到1000位了)
1.414213562373095048801688724209698078569671875376948073176679737990732478462
10703885038753432764157273501384623091229702492483605585073721264412149709993
58314132226659275055927557999505011527820605714701095599716059702745345968620
14728517418640889198609552329230484308714321450839762603627995251407989687253
39654633180882964062061525835239505474575028775996172983557522033753185701135
43746034084988471603868999706990048150305440277903164542478230684929369186215
80578463111596668713013015618568987237235288509264861249497715421833420428568
60601468247207714358548741556570696776537202264854470158588016207584749226572
26002085584466521458398893944370926591800311388246468157082630100594858704003
18648034219489727829064104507263688131373985525611732204024509122770022694112
75736272804957381089675040183698683684507257993647290607629969413804756548237
28997180326802474420629269124859052181004459842150591120249441341728531478105
80360337107730918286931471017111168391658172688941975871658215212822951848847
是不是感到震驚,代碼竟然如此短!?
是的,沒有看錯,就這麼一點點。
有興趣的小夥伴可以試試 https://tool.lu/coderunner/ 的在線編譯器;
左上角選擇 Python 然後複制上面的代碼,運行看看結果。(如下圖)
按照同樣的方式,大家是不是可以擴展出3次,5次.....等等的開方計算方式了?
總結
有時候思路正确了,所要做的反而就很少了!
我在心裡十分佩服前人的智慧與偉大!
一起努力,加油!
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!