一.問題描述:給定一個數,如何求它的平方根(不能使用內置函數,如sqrt()函數)。ios
二.題解:算法
這屬於比較經典的一道題目,一般有兩種方法:二分法和牛頓法,下面是詳細描述。編程
方法1:二分法,這是比較容易想到的一種方法。經過比較中間值與最終值的大小來改變中間值,最終在知足某個精度的狀況下返回這個中間值做爲最終結果。代碼以下:函數
#include<iostream> #include<cmath> using namespace std; //n是大於等於1的數 double MySqrt(double n) { double _max = n; //此處必定爲浮點數,不要用整數 double _min = 0.0; double p = 1e-5; //此處爲精度,當知足該精度時,返回該近似值 double mid = (_max + _min) / 2.0; while(fabs(mid * mid - n) > p)//此處是浮點數之差的絕對值與精度進行比較 { if(mid * mid < n) _min = mid; else if(mid * mid > n) _max = mid; else return mid; mid = (_max + _min) / 2.0; } return mid; } int main() { cout<<MySqrt(80)<<endl; }
很容易看出,該算法的時間複雜度爲O(logN),空間複雜度爲O(1)。並且最終結果的精度取決於精度p的設置。測試
須要注意的是,對於n小於1的時候,二分法就不適用了,由於mid必定是小於1的數,而mid*mid必定會變得更小,致使區間始終向0靠近(向左靠近),不符合二分法的特徵。spa
注:這裏面的變量類型都是浮點型!!code
方法2:1.牛頓法,牛頓迭代法(Newton's method)又稱爲牛頓-拉夫遜方法(Newton-Raphson method),它是牛頓在17世紀提出的一種在實數域和複數域上近似求解方程的方法。多數方程不存在求根公式,所以求精確根很是困難,甚至不可能,從而尋找方程的近似根就顯得特別重要。方法使用函數f(x)的泰勒級數的前面幾項來尋找方程f(x) = 0的根。牛頓迭代法是求方程根的重要方法之一,其最大優勢是在方程f(x) = 0的單根附近具備平方收斂,並且該法還能夠用來求方程的重根、復根。另外該方法普遍用於計算機編程中。blog
設r是f(x) = 0的根,選取x0做爲r初始近似值,過點(x0,f(x0))作曲線y = f(x)的切線L,L的方程爲y = f(x0)+f'(x0)(x-x0),求出L與x軸交點的橫座標 x1 = x0-f(x0)/f'(x0),稱x1爲r的一次近似值。圖片
過點(x1,f(x1))作曲線y = f(x)的切線,並求該切線與x軸交點的橫座標 x2 = x1-f(x1)/f'(x1),稱x2爲r的二次近似值。重複以上過程,得r的近似值序列,其中x(n+1)=x(n)-f(x(n))/f'(x(n)),稱爲r的n+1次近似值,上式稱爲牛頓迭代公式。get
2.而開根號的問題能夠看做求解f(x) = x2 - a = 0的根。
(1)在曲線f(x)=x^2-a上任取一點(x0,f(x0)),x0≠0,該點的切線方程爲:
(2)該切線與x軸的交點爲:
(3)不斷用新的交點來更新原來的交點(即逼近的過程)
根據牛頓迭代的原理,能夠獲得如下的迭代公式:
根據這個公式,可實現該算法,代碼以下:
#include<iostream> #include<cmath> using namespace std; double MySqrt(double n) { double x = 1.0;//設置初值 double p = 1e-5;//設置精度 while(fabs(x*x - n) > p) { x = (x + n / x) / 2.0; } return x; } int main() { cout<<MySqrt(82)<<endl; }
牛頓法同二分法同樣,時間複雜度爲O(logN),空間複雜度爲O(1)。牛頓法每次迭代的偏差都會至少小一半,因此複雜度最多是O(logN)。根據牛頓法的原理可知,迭代的次數越多,近似值越逼近真實值,固然咱們會經過設置精度來限制它的迭代次數。
三.牛頓法與二分法的比較:
1.我經過測試82的平方根來比較這兩種方法:
二分法:
牛頓法:
能夠看出,當我設置兩種算法的精度同樣時,二分法迭代次數爲24次,而牛頓法的迭代次數爲7次;且牛頓法的準確率更高。
因此說,牛頓法與二分法相比,速度更快、準確率更高。
2.牛頓法須要設置初值(即初始的x0),有時問題的答案的準確率很依賴於初值的設定(可參考:https://www.zhihu.com/question/20690553);而二分法不須要設置初值,因此穩定性較強。