博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
二分法03:x 的平方根
阅读量:3953 次
发布时间:2019-05-24

本文共 3380 字,大约阅读时间需要 11 分钟。

文章目录

x 的平方根

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4输出: 2

示例 2:

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

方法一:二分查找

由于 x x x 平方根的整数部分 ans \textit{ans} ans 是满足 k 2 ≤ x k^2 \leq x k2x的最大 k k k 值,因此我们可以对 k k k 进行二分查找,从而得到答案。

二分查找的下界为 0,上界可以粗略地设定为 x x x。在二分查找的每一步中,我们只需要比较中间元素 mid \textit{mid} mid 的平方与 x x x 的大小关系,并通过比较的结果调整上下界的范围。

在判断是否满足条件要用除,不能直接mid * mid,因为如果mid过大会溢出,除的话分2种,一种是x / mid < mid,说明 mid大了, 即最优解在[left,mid],压缩右边界,即right =mid

第2种情况是 x / mid >= mid,说明 mid 太小了,即最优解在[mid+1,right],那么就让下边界往上去一点, left = mid +1,为什么这里不减1呢?因为控制left总比x的根大一点,这样返回最终结果直接返回 left - 1即可,代码如下:

class Solution(object):    def mySqrt(self, x):        """        :type x: int        :rtype: int        """        if x==0 or x==1:            return x        left=0        right = x        while left
x/mid: # 最优解在`[left,mid]` right =mid else: # 最优解在`[mid+1,right]` left = mid +1 return left-1

复杂度分析

时间复杂度: O ( log ⁡ x ) O(\log x) O(logx) ,即为二分查找需要的次数。

空间复杂度: O ( 1 ) O(1) O(1)

方法二:牛顿迭代

,原理是使用函数 f ( x ) f(x) f(x) 的泰勒级数的前面几项来寻找方程 f ( x ) = 0 f(x)=0 f(x)=0 的根。

将函数 f ( x ) f(x) f(x) x 0 x_0 x0 处展开成泰勒级数:

f ( x ) = f ( x 0 ) + f ′ ( x 0 ) ( x − x 0 ) + 1 2 f ′ ′ ( x 0 ) ( x − x 0 ) 2 + … (5) f(x)=f(x_0)+f^{'}(x_0)(x-x_0)+\frac{1}{2}f^{''}(x_0)(x-x_0)^2+\dots \tag5 f(x)=f(x0)+f(x0)(xx0)+21f(x0)(xx0)2+(5)

取其线性部分,作为 f ( x ) f(x) f(x) 的近似,则可用 f ( x 0 ) + f ′ ( x 0 ) ( x − x 0 ) = 0 f(x_0)+f^{'}(x_0)(x-x_0)=0 f(x0)+f(x0)(xx0)=0 的解来近似 f ( x ) = 0 f(x)=0 f(x)=0 的解,其解为

x 1 = x 0 − f ( x 0 ) f ′ ( x 0 ) (6) x_1=x_0-\frac{f(x_0)}{f^{'}(x_0)}\tag6 x1=x0f(x0)f(x0)(6)

由于对 f ( x ) f(x) f(x) 的近似只是一阶展开,因此 r r r 并非 f ( x ) = 0 f(x)=0 f(x)=0 的解,只能说 f ( x 1 ) f(x_1) f(x1) f ( x 0 ) f(x_0) f(x0) 更接近0。于是,考虑迭代求解:

x n + 1 = x n − f ( x n ) f ′ ( x n ) (7) x_{n+1}=x_{n}-\frac{f(x_n)}{f^{'}(x_n)} \tag7 xn+1=xnf(xn)f(xn)(7)

由于牛顿法是基于当前位置的切线来确定下一次的位置,所以牛顿法又被很形象地称为是"切线法"。牛顿法的搜索路径(二维情况)如下图所示:

牛顿法搜索动态示例图:

img

为了叙述方便,我们用 C 表示待求出平方根的那个整数。显然,C 的平方根就是函数

y = f ( x ) = x 2 − C y = f(x) = x^2 - C y=f(x)=x2C

的零点。我们选择 x 0 = C x_0 = C x0=C 作为初始值。

f ′ ( x ) = 2 x f^{'}(x) = 2 x f(x)=2x

由7算式,新的迭代结果

x n + 1 = x n − x n 2 − C 2 x n = 1 2 ( x n + C x n ) x_{n+1}=x_{n}- \frac{x_n^2 - C}{2x_n} = \frac{1}{2}\left(x_n + \frac{C}{x_n}\right) xn+1=xn2xnxn2C=21(xn+xnC)

在进行 k k k 次迭代后, x k x_k xk 的值与真实的零点 s q r t C sqrt{C} sqrtC 足够接近,即可作为答案。

class Solution(object):    def mySqrt(self, x):        """        :type x: int        :rtype: int        """        if x==0 or x==1:            return x        C, x0 = float(x), float(x)        while True:            x_n = 0.5*(x0 +C/x0)            if abs(x0 - x_n)<1e-7:                break            x0 = x_n        return int(x0)

细节

为什么选择 x 0 = C x x_0 = Cx x0=Cx 作为初始值?

因为 y = x 2 − C y = x^2 - C y=x2C 有两个零点 − C -\sqrt{C} C C \sqrt{C} C 。如果我们取的初始值较小,可能会迭代到 − C -\sqrt{C} C 这个零点,而我们希望找到的是 C \sqrt{C} C 这个零点。因此选择 x 0 = C x_0 = C x0=C 作为初始值,每次迭代均有 x i + 1 < x i x_{i+1} < x_i xi+1<xi,零点 C \sqrt{C} C 在其左侧,所以我们一定会迭代到这个零点。

迭代到何时才算结束?

每一次迭代后,我们都会距离零点更进一步,所以当相邻两次迭代得到的交点非常接近时,我们就可以断定,此时的结果已经足够我们得到答案了。一般来说,可以判断相邻两次迭代的结果的差值是否小于一个极小的非负数 ϵ ϵ \epsilonϵ ϵϵ,其中 \epsilonϵ 一般可以取 1 0 − 7 10^{-7} 107

复杂度分析

时间复杂度: O ( log ⁡ x ) O(\log x) O(logx) ,此方法是二次收敛的,相较于二分查找更快。

空间复杂度: O ( 1 ) O(1) O(1)

参考

力扣(LeetCode) (leetcode-cn.com)]

《画解剑指 Offer 》

转载地址:http://pdyzi.baihongyu.com/

你可能感兴趣的文章
Python交互式数据分析报告框架~Dash介绍
查看>>
Chrome 浏览器 必知必会的小技巧
查看>>
Python奇技淫巧,看看你知道几个
查看>>
资源&教程 | Python数据分析,详细的学习路径
查看>>
白话AI:看懂深度学习真的那么难吗?初中数学,就用10分钟
查看>>
中国癌症大数据出来了!每年126万例癌症死亡本可避免
查看>>
超全的 Linux 机器的渗透测试命令备忘表,共16表128条命令
查看>>
代码传奇 | 明明可以靠颜值 却用代码把人类送上了月球的女人——Margaret Hamilton
查看>>
教你用Java来玩答题(百万英雄/冲刺大会等)
查看>>
用Python做跳一跳外挂太浪费了,这种技能让你目瞪口呆
查看>>
如何在Python中快速进行语料库搜索:近似最近邻算法
查看>>
比特币这么火热,看看这篇比特币初学者指南
查看>>
快收藏! 30 分钟包你学会 AWK
查看>>
各平台的推荐算法,太贴切了!
查看>>
一张图学会Python3
查看>>
500款各领域机器学习数据集,总有一个是你要找的
查看>>
2017年终奖调查出炉 程序员年终奖多少你绝对猜不到
查看>>
写给大数据开发初学者的话 | 附教程
查看>>
分享 :17款工具,让你的数据更美观
查看>>
不必再费心寻找,2017最全的开发干货就在这1067页PDF里
查看>>