给出非负整数x,范围0 <= x <=,要求在不使用任何内置指数函数和算符(pow(x, 0.5) x ** 0.5)的情况下返回x的算数平方根
思路无非就是1.模拟出指数函数迭代过程得出近似结果2.用其他函数替代平方根函数得到结果
这篇笔记会记录官方答案给出的三种题解
相关代码
🟦使用指数函数exp()和对数函数ln代替平方根函数
if(x == 0) cout << 0 << endl;
int ans = exp(0.5 * log(x));
cout << ((long long)(ans + 1) * (ans + 1) <= x ? ans + 1 : ans) << endl
;
log()是e为底的对数
log10()是10为底的对数
为什么最后还要比较ans+1和ans,是因为计算机无法存储浮点数的精确值(参考IEEE754)指数函数和对数函数的参数和返回值均为浮点数,因此运算过程中会存在误差。题解中给出了一个例子x=2147395600时,的计算结果和正确值46340相差
会导致取整之后得到46339(一般人也不会想到吧喂)
内置的exp()和log()都很快,可以将其时间复杂度视为O(1)
🟦二分查找
还是想找出平方结果小于等于x的数,但是用传统循环时间复杂度是O(n),这里用二分,初始范围设置在0到x之间
int l = 0, r = x, ans = -1;
while(l <= r){
int mid = (r - l) / 2 + l;
if((long long) mid * mid <= x){
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
cout << ans << endl;
可以把时间复杂度降到O(logx)
🟦牛顿迭代
将目标x视为C,整个问题能转换成求方程的零点
牛顿迭代实际上是借助泰勒级数,从初始值开始快速逼近,将一个任取的作为初始值,迭代开始
取在函数上的点做一条斜率为该点的导数
的直线,和横轴相交于
,
相比于
更靠进目标零点。多次迭代,就会得到一个和目标结果及其相近的值
在函数中,
那么关于
和
的直线方程可推算为
关联横轴与方程,可以得到的表达式
在进行数次迭代之后,得到的和要求的
足够接近
要注意的几点
初始值
的选择
初始的设置为C,因为方程
有两个零点
和
,如果选择较小的初始值结果可能会迭代到
上,初始的
设置为C不仅方便计算还能确保迭代的方向是正确的(你尽管赋值剩下的交给计算机)
迭代结束的判断条件
一般判断相邻两次迭代的结果差值是否小于一个非负整数,
可以取值为
或者
double C = x, x0 = x;
while(1){
double xi = 0.5*(x0 + C / x0);
if(fabs(x0 - xi) < 1e-7) break;
x0 = xi;
}
cout << (int)x0 << endl;
时间复杂度同样是O(logx)
小结:
网站分享了三种复杂度低于O(n)的方法,其中牛顿迭代我的收获很大,这只是对凸函数的处理,不知道后面是否能够遇到更合适的应用