牛顿迭代与二分查找开平方

牛顿迭代

不要被牛顿的大名唬住,牛顿迭代其实就是一种求近似解的方法。求解过程就是对曲线做切线,然后在切线与x轴交点,然后在这个点做x轴垂线,垂线到曲线交点继续做切线,一直重复上述步骤。然后切线与曲线交点就会慢慢的趋近于X轴与曲线交点。x轴与曲线交点就是曲线方程的根,但是直线方程的跟比较好计算,计算出最近的直线方程根,约等于曲线的根。大概做曲线切线的过程如下

牛顿迭代与二分查找开平方_第1张图片

公式推到过程

假设曲线 f(x) f ( x ) ,则曲线斜率 f(x) f ′ ( x )
曲线上 (xn,f(xn)) ( x n , f ( x n ) ) 点的切线方程 y=f(xn)(xxn)+f(xn) y = f ′ ( x n ) ( x − x n ) + f ( x n )
求上述方程的根(函数 f f 的一个根(或称零点)是 f f 的定义域 D D 中适合 f(x)=0 f ( x ) = 0 的元素 x x ),若求切线与 x x 轴焦点 x x ,则有以下方程 f(xn)(xxn)+f(xn)=0 f ′ ( x n ) ( x − x n ) + f ( x n ) = 0
解方程可得 x=xnf(xn)f(xn) x = x n − f ( x n ) f ′ ( x n )
所以切线的根为 xnf(xn)f(xn) x n − f ( x n ) f ′ ( x n )

由于下一个切线点是在上边求出点做 x x 轴垂线获得,可以推到出下一个点 xn+1=xnf(xn)f(xn) x n + 1 = x n − f ( x n ) f ′ ( x n )
依次计算 x1 x 1 x2 x 2 x3 x 3 ……,那么序列将无限逼近方程的根。

举例说明

例如求3的平方根 x=32 x = 3 2 x2=3 x 2 = 3
f(x)=x23 f ( x ) = x 2 − 3 所以 f(x)=2x f ′ ( x ) = 2 x
代入推导出的公式有
xn+1=xnx2n32xn=12(xn+2xn) x n + 1 = x n − x n 2 − 3 2 x n = 1 2 ( x n + 2 x n )

随便一个迭代的初始值,例如 x0=1 x 0 = 1 ,代入上面的式子迭代有
x1=12(x0+2x0)=12(1+21)=1.5 x 1 = 1 2 ( x 0 + 2 x 0 ) = 1 2 ( 1 + 2 1 ) = 1.5
x2=12(x1+2x1)=12(1.5+21.5)=1.416666... x 2 = 1 2 ( x 1 + 2 x 1 ) = 1 2 ( 1.5 + 2 1.5 ) = 1.416666...
x3=12(x2+2x2)=12(1.416666+21.416666)=1.414218... x 3 = 1 2 ( x 2 + 2 x 2 ) = 1 2 ( 1.416666 + 2 1.416666 ) = 1.414218...
……

a a 的平方根 x=a2 x = a 2 x2=a x 2 = a
f(x)=x2a f ( x ) = x 2 − a 所以 f(x)=2x f ′ ( x ) = 2 x
代入推导出的公式有
xn+1=xnx2na2xn=12(xn+axn) x n + 1 = x n − x n 2 − a 2 x n = 1 2 ( x n + a x n )

a a k k 次方根 x=ak x = a k xk=a x k = a
f(x)=xka f ( x ) = x k − a 所以 f(x)=kxk1 f ′ ( x ) = k x k − 1
代入推导的公式有
xn+1=xnxknakxk1n=k1kxn+akxk1n x n + 1 = x n − x n k − a k x n k − 1 = k − 1 k x n + a k x n k − 1

二分查找

二分查找(折半查找),就是每次都在序列中找最中间的那个元素,根据元素与目标元素大小比较在剩下的一般继续查找,如此循环值到找到为止。
二分查找比较常见的场景就是在一个有序数组中查找某个值,代码一般如下

private int binarySearch(long[] a, int fromIndex, int toIndex,long key) {
   int low = fromIndex;
    int high = toIndex - 1;

    while (low <= high) {
        int mid = (low + high) >>> 1;
        long midVal = a[mid];

        if (midVal < key)
            low = mid + 1;
        else if (midVal > key)
            high = mid - 1;
        else
            return mid; // key found
    }
    return -(low + 1);  // key not found.
}

当然也可以用二分查找来求平方根,其实就是把上边的查找值变成找平方根而已。比较条件换成 mid2 m i d 2 num n u m 的比较。

求平方根算法具体算法(Java)

private static final double deviation = 0.00001;//求值精度

/**
 * 返回平方根,判断如果正数正好是平方根就返回,否则返回小数的
 * @param x
 * @param mid
 * @return
 */
private double getSquare(double x, double mid) {
    long a = Math.round(mid);
    if (a * a == x) {
        return a;
    } else {
        return mid;
    }
}

/**
 * 牛顿迭代求平方根
 * @param num
 * @return
 */
public double sqrtNewton(double num) {
    double x = num;
    double nextX;
    while (true) {
        nextX = x - (x * x - num) / (2 * x);
        if (Math.abs(nextX * nextX - num) <= deviation) {
            return getSquare(num, nextX);
        }
        x = nextX;
    }
}


/**
 * 二分查找求平方根
 * @param num
 * @return
 */
public double sqrtBinary(double num) {
    if (num == 0 || num == 1) {
        return num;
    } else {
        double start = 0;
        double end = num;
        double mid;
        while (true) {
            mid = (start + end) / 2;
            if (mid * mid == num || Math.abs(mid * mid - num) <= deviation) {
                return getSquare(num, mid);
            } else if (mid < num / mid) {
                start = mid;
            } else {
                end = mid;
            }
        }

    }
}

我的博客:http://yuntaoblog.com/2018/03/29/牛顿迭代与二分查找开平方/

你可能感兴趣的:(算法)