Sqrt(x)
没错,有几天没写LeetCode了,就遇上了sqrt函数的编写,这是不是很简单呢?于是我很快的想出了遍历的方法,但是马上又想到了若是一个最大的整数位测试用例呢?那么效率太低了吧,于是我以效率为出发点又想到了二分法,使用二分法自己测试了几个,感觉还可以,于是就提交了,代码如下:
class Solution
{
public:
int mySqrt(int x)
{
//二分法
assert(x >= 0);
//1.特殊情况
if (x == 0 || x == 1)
{
return x;
}
//2.一般情况
int left = 0;
int right = x;
int mid = (left + right) / 2;
while (mid*mid > x || (mid+1)*(mid+1) <= x)
{
if (mid * mid > x)
{
right = mid;
}
else if ( (mid + 1) * (mid + 1) <= x)
{
left = mid;
}
mid = (left + right) / 2;
}
return mid;
}
};
测试用例卡在了2147395599上面,结果如下:
出现这种结果说明二分法还是不够优化,那么我们所能想到的办法还有什么呢?我感觉真的是想不出来,于是我在网上看了一些文章,将关于sqrt函数的问题,于是我找到了一种求解方法,利用到的思想是”牛顿迭代法快速寻找平方根“,其思想是利用高数中的思想:求 X^2 - a = 0 的根,这些思想若是没有学习过真的是想不出来的,我是参考一篇文章,所以最后我会将这篇文章贴出来,分享给大家。
使用牛顿迭代快速寻找平方根的思想,我们可以写出下列代码:
class Solution
{
public:
int mySqrt(int x)
{
//牛顿迭代法
assert(x >= 0);
if (x == 0 || x == 1)
{
return x;
}
float val = x; //最终
float last; //保存上一个计算的值
do{
last = val;
val = (val + x / val) / 2;
} while (abs(val - last) > 0.01); //因为这个题的最终结果是整数,所以我们可以将精度提高点
int ret = (int)val;
if (ret*ret > x)
{
return ret - 1;
}
else
{
return ret;
}
}
};
这个函数最终被接收了,结果如下:
其实到这里还没完呢?有些人真的是大神,他们只要两步就可以求出一个数的平方根,他的思路我还没弄明白,因为源码是某游戏引擎的代码,不过这里还是将他的代码贴出来,跟大家分享一下:
class Solution
{
public:
int mySqrt(int x)
{
//数学大神根据牛顿迭代法来求推导出的神奇方法
assert(x >= 0);
if (x == 0 || x == 1)
{
return x;
}
float tmp = x;
float xhalf = 0.5f*tmp;
int i = *(int*)&tmp;
i = 0x5f375a86 - (i >> 1); // 这一步是关键
tmp = *(float*)&i;
tmp = tmp*(1.5f - xhalf*tmp*tmp);
tmp = tmp*(1.5f - xhalf*tmp*tmp);
tmp = tmp*(1.5f - xhalf*tmp*tmp);
int ret = 1 / tmp;
if (ret*ret > x)
{
return ret - 1;
}
return ret;
}
};
这种方法是正确的,因为它是游戏引擎中的源码,所以不用验证都可以知道其正确性。
我个人觉得这种类型的题真的是很吃数学功底,也道题很彻底的让我感受到了数学的强大,让我深受感触。下面我就给出我看过的文章的出处:参考文章