LeetCode[Math]----Sqrt(x)

Implement int sqrt(int x).

Compute and return the square root of x, where x is guaranteed to be a non-negative integer.

Since the return type is an integer, the decimal digits are truncated and only the integer part of the result is returned.

Example 1:

Input: 4
Output: 2

Example 2:

Input: 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since 
             the decimal part is truncated, 2 is returned.

分析:

题目来源于LeetCode 第69题(https://leetcode.com/problems/sqrtx/),据说这道题有好几种做法,先记录下今天的方法。

求一个数的平方根,但是不需要求得精确值(不需要小数),只需要得到一个整数值就可以了。

拿到题目后会想,最简单肯定是给定x,对小于等于x的所有整数遍历一下就得到了,不过这样效率比较差,有没有更好的方法呢?

第一种想法是尝试利用sqrt(ab) < (a+b)/2的关系,但是好像没有什么用。第二种想法是在第一种想法的基础上发现一个规律:

当x >= t**2 时,只要尝试小于等于 x/t 的整数就好了,比如当x >= 16时,应该尝试 x/4 的整数,但是这种思路需要尝试x的范围,也不是很方便。最后在第二种想法的基础上想到可以利用下二分法进行尝试,时间复杂度为logn,还是可以接受的。

具体思路是,用二分法按mid值的平方大于或小于x来逼近那个整数,而当mid值等于左右区间中间点时,该mid值即为所要的整数。

代码:

class Solution(object):
    def mySqrt(self, x):
        """
        :type x: int
        :rtype: int
        """
        if x == 0:
            return 0
        if x <= 3:
            return 1
        left, right = 1, x
        mid = -1
        while mid != (left + right) / 2:
            mid = (left + right) / 2
            if mid ** 2 > x:
                right = mid
            else:
                left = mid
        return mid

2018.11.21补充牛顿法:

之前就听说过这道题可以用牛顿法来求解。牛顿法可以用来求高阶方程的根,我们有两种方式能得到牛顿法的迭代公式,第一种是对f(x)进行在x0点泰勒展开,忽略掉二阶及高阶项,令 0=f(x*)≈f(x0)+f'(x0)(x* - x0) ,近似得到 x*≈x1=x0 - f(x0)/f'(x0),再在x1点进行泰勒展开,得到 x2=x1 - f(x1)/f'(x1),由此得到迭代公式 xn_1 = xn - f(xn) / f'(xn_1) ,通过迭代公式可以快速逼近 x* . 第二种方式是通过切线,构建切线,以切线近似f(x), 同样得到迭代公式,而当切线无限逼近(x*, f(x*)) 时,切线的零点可以近似看做f(x)的零点。当然牛顿法使用有一个前提,就是 f(x) 满足二阶可导,此时牛顿法可以收敛。

按牛顿法写了源码如下:

    def mySqrt(self, x):
        """
        :type x: int
        :rtype: int
        """
        if x == 0:
            return 0
        if x <= 3:
            return 1
        xn_1, xn = 1000, 8.0
        while True:
            xn_1 = (xn**2 + x) / (2 * xn)
            if int(xn_1) == int(xn):
                break
            xn = xn_1
        return int(xn_1)

在查看了其他博客代码后,优化代码如下:

    def mySqrt(self, x):
        xn = x
        while xn * xn > x:
            xn = (xn + x / xn) / 2
        return xn

直观感受,牛顿法的迭代效率应该会优于二分法,毕竟牛顿法还考虑到了导数,是一种非线性的查找。不过实际提交运行发现,三个代码的运行效率并没有太大差别。

猜你喜欢

转载自blog.csdn.net/whiterbear/article/details/83818738