Leetcodex的平方根算法与分析

@Leetcodex的平方根

简单的数学问题,这并不是一道考验思路的题,而是考验算法运用的问题,怎么更快的解决才是王道!请看题干:

实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:

输入: 4
输出: 2

示例 2:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842…,
由于返回类型是整数,小数部分将被舍去。

首先我们第一份代码给到笔者投机取巧通过的一份代码,请看:

class Solution {
public:
    int mySqrt(int x) {
        long long ans;
        double t=sqrt(x);
        ans=(long long)t;
        return ans;
    }
};

在这里插入图片描述
哇塞,直接把要实现的函数给用进了程序里面,再来一个强制类型转换,居然搞定了。
其实不用强制转成int应该也能通过,但是为了尊重一下题干,笔者觉得还是写一下为好。

那么言归正传,本题的正宗思想是什么?要找到一个确定肯定以及绝对存在的答案,为什么不直接使用查找算法呢?于是笔者来了一套二分查找,请看代码:

class Solution {
public:
    int mySqrt(int x) {
        long long m=0;
        long long n=x/2+1;
        long long ans=0;
        while(m<=n){
            long long mid=(m+n)/2;
            ans=mid*mid;
            if(ans==x)
                return mid;
            else if(ans<x)
                m=mid+1;
            else 
                n=mid-1;
        }
        return n;
    }
};

在这里插入图片描述
这份代码看似简单,其实暗藏玄机。由于使用int类型可能无法存下某些数据,故干脆直接掏出long long来的简单。定义头尾中间,最终结果是ans,将其与x比较,如果小了就左端变成mid加一,如果大了就右端变成mid减一,判断简单又好用,人见人爱。

那么如何解决要取整的问题呢,比如示例2中的情况,其中8的平方根并不是一个整数。不用慌,其实这个问题已经不小心解决了。由于循环是执行到m<=n这个情况不满足为止,此时说明n经过最后一次减一之后已经小于m了,说明减一之前的n值和m值都不满足,并且是过大,而m一路遍历过来发现目前的n值是偏小,所以此时的这个偏小的n值正是取整之后得到的答案,直接输出即可通过。

但是真正的大佬早就看不上二分法了,牛顿迭代法才是真正最最高端的解法,请看代码:

class Solution {
public:
    int mySqrt(int x) {
        long long ans=x/2+1;
        while (ans*ans>x)
            ans=(ans+x/ans)/2;
        return ans;
    }
};

在这里插入图片描述
牛顿解法公式一目了然:ans=(ans+x/ans)/2 代码写完甚至有些意犹未尽,然而问题已经解决了。
所以这个方法到底是怎么回事?牛顿迭代法又称为牛顿-拉夫逊(拉弗森)方法,是一个快速寻找平方根的解法,具体可以参考两位大佬的blog
https://blog.csdn.net/ccnt_2012/article/details/81837154,以及https://www.cnblogs.com/qlky/p/7735145.html
里面有对其详细的分析过程,在这里我挑挑重点谈一谈:

要计算x² = n的解,令f(x)=x²-n,相当于求f(x)=0的解,画出坐标图像,取x0,若x0不是解,做一个经过(x0,f(x0))点的切线,与x轴的交点为x1,再判断x1是否是解,如果x1也不是解,做一个经过(x1,f(x1))点的切线,与x轴的交点为x2,以此类推,可以得到无限趋近于f(x)=0的解xi。
对xi的判断有两种方法,一是直接计算f(xi)的值是否为0,二是判断前后两个解xi和xi-1是否无限接近。经过(xi, f(xi))这个点的切线方程为f(x) = f(xi) + f’(xi)(x - xi),其中f’(x)为导数,即2x。令切线方程等于0,得xi+1=xi - f(xi) / f’(xi),继续化简得 xi+1=xi - (xi² - n) / (2xi) = xi - xi / 2 + n / (2xi) = xi / 2 + n / 2xi = (xi + n/xi) / 2。

那么实际应用到本题之中,这个最先取得的x0当然就是x/2+1这个值,从这个值往前进行判断,这种判断方法能避免之前二分查找时的挨个判断的低效率,但是所耗时间也不会因为这个减少特别多,依据之前的公式推断即可得到一个最为借鉴的值,最后一个数的判断与二分查找的判断原理类似,循环判断条件是ans*ans>x,所以也是能直接得到正确答案。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值