开方运算实现-sqrt(x)--阿里面试题

问题描述

不使用库函数sqrt()实现对一个数的开方运算,精度为小数点后十位(1e-10)

input 2
output 1.4142135623
思路:牛顿迭代法和二分法

牛顿迭代法(牛顿-拉弗森方法)

产生背景:五次及以上的多项式没有根式解(二次函数的万能公式),被伽罗瓦用群论做出的最著名结论
驱动: 然而工作中有很多高次求解的需求,所以就出现了牛顿迭代法
原理:

  • 切线是曲线的线性逼近,数学中的切线方程
  • 两人的直觉:在曲线上随机找一个点做切线,设该切线与X轴的交点为x1,然后再做点(x1, f(x1))的切线,设该切线与X轴的交点为x2,然后做点(x2, f(x2))的切线……会发现交点序列x1, x2, x3……不断的逼近f(x)的零点,这样只要迭代的次数够多就可以得到零点的近似解。

公式: 利用切线方程求解,切线方程为 Y - f(Xn) = f’(Xn)(X - Xn) ;令y=0,可得 Xn+1 = Xn - f(Xn) / f’(Xn)
收敛性:

收敛的充分条件:若f 二阶可导,那么在待求的零点x 周围存在一个区域,只要起始点x_{0} 位于这个邻近区域内,那么牛顿-拉弗森方法必定收敛。

  • 驻点:如果起始点选为驻点则无法得到最终结果
  • 越来越远离的不收敛,如x^(1/3),原因是在零点处的导函数不存在
  • 循环震荡不收敛,如|x|(1/2)原因也是在零点处的导数不存在;但是不一定f’(0) 不存在就无法用牛顿-拉弗森方法求解,比如f(x)=|x|^(2/3)依然可以用牛顿-拉弗森方法。
  • 不能完整求出所有跟,如果存在多个零点,起始位置的选择会影响最后的结果
    应用: 求平方根,x^2 = A,只需要带入上面迭代公式即可,得到Xn+1 = (A/Xn + Xn) / 2。

参考:如何通俗易懂地理解牛顿迭代法

public static double sqrtByNewton(double n){
        double result = 1;
        while(Math.abs(result * result - n) > epr){
        	//根据迭代公式直接进行计算
            result = (n / result + result) / 2;
        }
        return result;
    }

二分法

二分法的思路很简单,就是在0-n之间寻找结果,每轮迭代都更新搜索空间。
需要注意的是迭代结束的条件,一种可以通过结果的平方与原数比较,一种是通过上一次的结果与这次的结果进行比较。

public static double sqrtByDo(double n){
        double left = 0;
        double right = n;
        //保存上一次计算结果
        double last ;
        // 这种写法可以防止数据溢出
        double mid = left + (right - left) / 2;
        do{
            // 如果所求的值较大更新右边界
            //否则更新左边界
            if(mid > n / mid){
                right = mid;
            }else{
                left = mid;
            }
            last = mid;
            mid = left + (right - left) / 2;
        }while(Math.abs(last - mid) > epr);
        return mid;
    }

    public static double sqrtByWhile(double n){
        double left = 0;
        double right = n;
        double mid = left + (right - left) / 2;
        while (Math.abs(n - mid * mid) > epr){
            if(n / mid > mid){
                left = mid;
            }else{
                right = mid;
            }
            mid = left + (right - left) / 2;
        }
        return mid;
    }

调用一些其他的库函数

在Python中可以通过下面的方式间接进行开方

  • pow(n, 0.5)
  • n ** 0.5

java中关于sqrt的说明

java源码中并没有给出该函数的具体实现,原因是其是一个HotSpotIntrinsicCandidate注解的函数,表明该函数在HotSpot层通过指令的方式进行高效实现。

    @HotSpotIntrinsicCandidate
    public static double sqrt(double a) {
        return StrictMath.sqrt(a); // default impl. delegates to StrictMath
                                   // Note that hardware sqrt instructions
                                   // frequently can be directly used by JITs
                                   // and should be much faster than doing
                                   // Math.sqrt in software.
    }
    //实际调用函数
    @HotSpotIntrinsicCandidate
    public static native double sqrt(double a);

你可能感兴趣的:(面试思考)