69. Sqrt(x)

Total Accepted: 93296 Total Submissions: 368340 Difficulty: Medium

提交网址: https://leetcode.com/problems/sqrtx/

Implement int sqrt(int x).

Compute and return the square root of x.

分析:

解法1:牛顿迭代法(牛顿切线法)

Newton's Method(牛顿切线法)是由艾萨克·牛顿在《流数法》(Method of Fluxions,1671年完成,在牛顿死后的1736年公开发表)中最早提出的。约瑟夫·拉弗森也曾于1690年在Analysis Aequationum中提出此方法。它是牛顿在17世纪提出的一种在实数域和复数域上近似求解方程的方法。

蓝线表示方程f(x)而红线表示切线. 可以看出\(x_{n+1}\)\(x_n\)更靠近f所要求的根x.

既然牛顿迭代法可以用来求解方程的根,那么不妨以方程\(x^2=n\)为例,来试着求解它的根。为此。令\(f(x) = x^2 - n\), 也就是相当于求解f(x)=0的解,如上图所示。 

       首先随便找一个初始值\(x_0\),如果\(x_0\)不是解,做一个经过\((x_0,f( x_0))\)这个点的切线,与轴的交点为\(x_1\)。同理,如果\(x_1\)不是解,做一个经过\((x_1,f( x_1))\)这个点的切线,与轴的交点为\(x_2\)。 以此类推... 以这样的方式得到的会无限趋近于f(x)=0的解。 

判断\(x_i\)是否是f(x)=0的解有两种方法:1. 直接计算的值判断\(f(x_i)\)是否为0;2. 判断f(x)=0前后紧邻的两个解是否无限接近。 

经过这个点\((x_i, f(x_i))\)的切线方程为 \(f(x) = f(x_i) + f'(x_i)(x - x_i)\)

其中,\(f'(x_i)\)为\(f(x)\)的导数,本题中导数为\(2x\)。令切线方程等于0 (纵轴截距取0),即可求出:

\(x_{i+1}=x_i - \frac{f(x_i)}{f'(x_i)}\)

代入\(f(x) = x^2 - n\),继续化简:

\(x_{i+1}=x_i -\frac{x_i^2 - n}{2x_i} = x_i - \frac{x_i}{2} + \frac{n}{2x_i} = \frac{x_i}{2} + \frac{n}{2x_i}\)

基于上述迭代公式,可以给出了一个求平方根的算法。事实上,这也的确是很多语言中内置的开平方函数的实现方法。牛顿迭代法也同样适用于求解其他多次方程的解。

已AC代码:

#include <cstdio>
#include<climits>
#include<cmath>
using namespace std;
class Solution {
public:
int mySqrt(int x) {
if(x < 0) return INT_MIN;
if(x == 0) return 0;
double pre = 0; // res和pre是邻近的两次迭代结果,也可用变量adj表示邻近的值
double res = 1; // 在1附近开始找,迭代逼近目标值
while(abs(res-pre) > 0.000001) // 判断条件改为res-pre > 0.000001 || res-pre < -0.000001后,运行时间不变
{
pre = res;
res = (res + x/res)/2.0;
}
return int(res); // 返回值要求为int,需强制转换
}
};
// 下面为测试
int main()
{
int x1=7;
int x2=2222147483648;
int x3=-5;
Solution sol;
int res1=sol.mySqrt(x1);
int res2=sol.mySqrt(x2);
int res3=sol.mySqrt(x3);
printf("%d \n", res1);
printf("%d \n", res2);
printf("%d \n", res3);
return 0;
}

P.S:本题是求解整数的平方根,并且返回值也是整型。在上述代码基础上稍微做修改,就可以同样适用于double(仅限方法1)。

#include <cstdio>
#include<climits>
#include<cmath>
using namespace std;
class Solution {
public:
double mySqrt(double x) {
if(x < 0) return INT_MIN;
if(x == 0) return 0;
double pre = 0;
double res = 1; // 所求值为double时,迭代的初始值不能为0
// double res = 0.000001;
// double next = 1; // res和pre是连续两次的迭代结果(邻近值)
while(abs(res-pre) > 0.000001) // 判断条件改为res-pre > 0.000001 || res-pre < -0.000001后,运行时间不变
{
pre = res;
res = (res + x/res)/2.0;
}
return (res);
}
};
// 下面为测试
int main()
{
double x1=7;
double x2=2222147483648;
double x3=-5;
Solution sol;
double res1=sol.mySqrt(x1);
double res2=sol.mySqrt(x2);
double res3=sol.mySqrt(x3);
printf("%lf \n", res1);
printf("%lf \n", res2);
printf("%lf \n", res3);
return 0;
}

PS: 由于所求值为double时,迭代的初始值不能为0。此代码中pre和res可以用res和next替换,见注释部分,当然循环中也得将pre换为next

解法2:二分搜索法

对于一个非负数n,它的平方根取整 \(\lfloor \sqrt(x) \rfloor \leq (\lfloor \frac{x}{2} \rfloor +1)\),如下图所示,有x=1、2、4共3个整数交点,x>4以后\(\lfloor \sqrt(x) \rfloor \) 恒小于\(\lfloor \frac{x}{2} \rfloor +1\).

上图可在浏览器的新标签中打开,高清的

由于int sqrt(int x)接受的参数与返回值均为int型,故⌊√x⌋ ≤ (⌊x/2⌋+1)即等价于强数据类型语言(比如:C++、C、Java等)中的√x(目标值)≤ x/2+1 (x为自然数,非负整数). 于是在[0, x/2+1]这个范围内进行二分搜索,可以求出n的int型平方根,mid=(low+up)/2,其初值为x/2,结果应在[low, up]的mid或up处取得。如果用弱数据类型的语言(比如:PHP、Python、JavaScript等)实现此方法,需先自行ceiling或ceil进行下取整!

但此法不适用于double,因为此法利用了int型的特点。

AC代码:

#include <cstdio>
#include<climits>
using namespace std;
class Solution {
public:
int mySqrt(int x) {
if(x<0) return INT_MIN;
long long low=0;
long long up=x;
while(low <= up)
{
long long mid=(low+up)/2; // 取中间值mid,在此处如果改为位运算居然使程序变慢了!
long long square=mid*mid;
if(x==square) return mid; // 目标值等于mid处平方,提前退出循环出口
else if(x>square) low=mid+1; // 目标值大于mid处平方,在开区间(mid, up]中找,下界low的值调整为mid-1
else up=mid-1; // 目标值小于mid处平方,在开区间[low, mid)中找,上界up的值调整为mid+1
}
return up;
}
};
// 下面为测试
int main()
{
int x1=7;
int x2=2222147483648;
int x3=-5;
Solution sol;
int res1=sol.mySqrt(x1);
int res2=sol.mySqrt(x2);
int res3=sol.mySqrt(x3);
printf("%d \n", res1);
printf("%d \n", res2);
printf("%d \n", res3);
return 0;
}

此代码运行时间为8 ms,打败了39.64%的C++提交,除以2改成右移1位后,反而变慢了,12 ms,只打败了4.39%的C++提交...

相关链接:

http://www.cnblogs.com/AnnieKim/archive/2013/04/18/3028607.html (方法1代码测试未通过,方法2顺利)

http://blog.csdn.net/baimafujinji/article/details/50390841 (参考了循环的出口条件)

C++版 - Leetcode 69. Sqrt(x) 解题报告【C库函数sqrt(x)模拟-求平方根】的更多相关文章

  1. C#版 - LeetCode 148. Sort List 解题报告(归并排序小结)

    leetcode 148. Sort List 提交网址: https://leetcode.com/problems/sort-list/  Total Accepted: 68702 Total ...

  2. C++版 - Leetcode 400. Nth Digit解题报告

    leetcode 400. Nth Digit 在线提交网址: https://leetcode.com/problems/nth-digit/ Total Accepted: 4356 Total ...

  3. LeetCode 1 Two Sum 解题报告

    LeetCode 1 Two Sum 解题报告 偶然间听见leetcode这个平台,这里面题量也不是很多200多题,打算平时有空在研究生期间就刷完,跟跟多的练习算法的人进行交流思想,一定的ACM算法积 ...

  4. 【LeetCode】Permutations II 解题报告

    [题目] Given a collection of numbers that might contain duplicates, return all possible unique permuta ...

  5. 【LeetCode】Island Perimeter 解题报告

    [LeetCode]Island Perimeter 解题报告 [LeetCode] https://leetcode.com/problems/island-perimeter/ Total Acc ...

  6. 【LeetCode】01 Matrix 解题报告

    [LeetCode]01 Matrix 解题报告 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/problems/01-matrix/#/descripti ...

  7. 【LeetCode】Largest Number 解题报告

    [LeetCode]Largest Number 解题报告 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/problems/largest-number/# ...

  8. 【LeetCode】Gas Station 解题报告

    [LeetCode]Gas Station 解题报告 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/problems/gas-station/#/descr ...

  9. 【LeetCode】120. Triangle 解题报告(Python)

    [LeetCode]120. Triangle 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址htt ...

随机推荐

  1. springmvc 简单使用

    一.配置(使用)流程 1.新建maven工程,在pom.xml中导入相关包,重要的是springmvc包,servlet包,jstl包 <dependencies>             ...

  2. Codeforces Round #555 (Div. 3) E. Minimum Array

    题意:b数组可以自由排序,c[i]=(a[i]+b[i])%n. 题目中要求c数组的字典序是最小的.那么我们需要尽量满足前面的c[i],才能使字典序最小. 我们知道a[i]和b[i]都是[0,n-1] ...

  3. SAS 函数

    SAS 函数 SAS函数是编程语言的一个组件,可接受参数.执行计算或进行其他操作并返回值.返回值是字符型或数值型的结果,可用于赋值语句或 表达式中.SAS包含很多函数,也可以自定义函数.在BASE S ...

  4. vuex的mutations传值

    mutations是要通过方法触发的,用于更改store里的数据的.this.$store.commit("mutationsName") 例子: store.js import ...

  5. tg代理

    安装 wget -N --no-check-certificate https://raw.githubusercontent.com/FunctionClub/MTProxy-Bash/master ...

  6. java构建树形菜单递归工具类

    1.设计菜单实体 import java.util.List; public class Menu { //菜单id private Long id; //父节点id private Long par ...

  7. Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes

    Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes 题目连接: http://code ...

  8. vue组件自定义属性命名

    今天自己写vue组件demo的时候发现一个有趣的问题:vue组件自定义属性命名不支持用驼峰命名! 上面图示为正常情况下的自定义属性,没有任何问题. 但是一旦出现自定义属性中包含了大写字母,则如下图所示 ...

  9. Python基础-数据类型-转摘

    1.数字 2 是一个整数的例子.长整数 不过是大一些的整数.3.23和52.3E-4是浮点数的例子.E标记表示10的幂.在这里,52.3E-4表示52.3 * 10-4.(-5+4j)和(2.3-4. ...

  10. 段的性能统计信息v$segment_statistics

    v$segment_statistics视图记录了段的统计信息 简单的几个字段就不说了,就说最后三个吧 STATISTIC_NAME,STATISTIC#,VALUE记录了发生在表上的操作 SYS @ ...