1. 程式人生 > >Implement int sqrt(int x).

Implement int sqrt(int x).

求n的平方根,先假設一猜測值X0 = 1,然後根據以下公式求出X1,再將X1代入公式右邊,繼續求出X2…通過有效次迭代後即可求出n的平方根,Xk+1

x_(k+1)=1/2(x_k+n/(x_k))

先讓我們來驗證下這個巧妙的方法準確性,來算下2的平方根 (Computed by Mathomatic)

1-> x_new = ( x_old + y/x_old )/2 y (x_old + -----) x_old #1: x_new = --------------- 2 1-> calculate x_old 1 Enter y: 2 Enter initial x_old: 1 x_new = 1.5 1-> calculate x_old 2 Enter y: 2 Enter initial x_old: 1 x_new = 1.4166666666667
1-> calculate x_old 3 Enter y: 2 Enter initial x_old: 1 x_new = 1.4142156862745 1-> calculate x_old 10 Enter y: 2 Enter initial x_old: 1 Convergence reached after 6 iterations. x_new = 1.4142135623731 ...

可見,隨著迭代次數的增加,運算值會愈發接近真實值。

於是可以得到程式碼段:

double abs_math(double num)
{
    return num<0?-num:num;      //取絕對值
}
class Solution{
    public:
    int mySqrt(int x)
    {
        double g = x;
        while(abs_math(g*g-x)>0.000001){
            g = (g + x / g ) / 2;
        }
        return g;
    }
}
PS:後來在leetcode上提交後發現用了6ms只擊敗了百分之10.13的其他使用者,一怒之下一同分析刪掉ABS函式,仍然沒變化,等週末有空好好的研究下推導,再次刷到這個題的時候再考慮更好的方法

接下來是相關的數理推導:

簡單推導

假設f(x)是關於X的函式:

An illustration of one iteration of Newton's method

求出f(x)的一階導,即斜率:

f'(x_{n}) = \frac{ \mathrm{rise} }{ \mathrm{run} } = \frac{ \mathrm{\Delta y} }{ \mathrm{\Delta x} } = \frac{ f( x_{n} ) - 0 }{ x_{n} - x_{n+1} } = \frac{0 - f(x_{n})}{(x_{n+1} - x_{n})}\,\!

簡化等式得到:

x_(n+1)=x_n-(f(x_n))/(f^'(x_n))

然後利用得到的最終式進行迭代運算直至求到一個比較精確的滿意值,為什麼可以用迭代法呢?理由是中值定理(Intermediate Value Theorem):

如果f函式在閉區間[a,b]內連續,必存在一點x使得f(x) = cc是函式f在閉區間[a,b]

內的一點

我們先猜測一X初始值,例如1,當然地球人都知道除了1本身之外任何數的平方根都不會是1。然後代入初始值,通過迭代運算不斷推進,逐步靠近精確值,直到得到我們主觀認為比較滿意的值為止。例如要求768的平方根,因為252 = 625,而302 = 900,我們可先代入一猜測值26,然後迭代運算,得到較精確值:27.7128。

回到我們最開始的那個求2次方根的公式,令x2 = n,假設一關於X的函式f(x)為:

f(X) = X2 - n

f(X)的一階導為:

f'(X) = 2X

代入前面求到的最終式中:

Xk+1 = Xk - (Xk2 - n)/2Xk

化簡即得到我們最初提到的那個求平方根的神奇公式了:

x_(k+1)=1/2(x_k+n/(x_k))

用泰勒公式推導

The Art and Science of C一書中有用到泰勒公式求平方根的演算法,其實牛頓迭代法也可以看作是泰勒公式(Taylor Series)的簡化,先回顧下泰勒公式:

f(x_0+epsilon)=f(x_0)+f^'(x_0)epsilon+1/2f^('')(x_0)epsilon^2+....

僅保留等式右邊前兩項:

f(x_0+epsilon) approx f(x_0)+f^'(x_0)epsilon.

f(X0+ε) = 0,得到:

epsilon_0=-(f(x_0))/(f^'(x_0))

再令X1 = X0 + ε0,得到ε1…依此類推可知:

epsilon_n=-(f(x_n))/(f^'(x_n))

轉化為:

x_(n+1)=x_n-(f(x_n))/(f^'(x_n))

引申

從推導來看,其實牛頓迭代法不僅可以用來求平方根,還可以求立方根,甚至更復雜的運算。