欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

sqrt函数实现——二分法、牛顿迭代法

程序员文章站 2022-06-03 12:26:55
...

在leetcode练习时,碰到一道经典的面试题,如何实现sqrt()开平方函数。当然,很简单的是调用系统函数,但是难道不能自己实现这个函数的功能吗?于是一番思索和查阅资料,看到下面的方法。

二分法求解

二分法这个应该很熟悉,在二分查找算法中就有具体的体现。应用在此题上,也是合适不过的。
首先分析一下这道题:
实现sqrt函数功能,求一个数的开平方,即求f(x)=x2Nf(x) = x^2 - N这个函数f(x)=0f(x) = 0的非负解。

  • 原理
    二分法的原理很简单,就是通过缩减区间来确定解的位置。通过图来说明:
    sqrt函数实现——二分法、牛顿迭代法
  • 实现步骤
    • 选择区间[a, b],f(a)与f(b)异号。
    • 获得区间中值mid,以及f(mid)值。
    • 若f(a) * f(mid) < 0,即f(a)与f(mid)异号,取新区间[a, mid]。相反,取区间[mid, b]。
    • 重复上两步操作,直到达到类型精度停止。
  • 代码实现
int BisectionSqrt(int x)
{
	double low = 0, high = x + 0.25, mid = (low + high) / 2;
	while (mid - low > DBL_EPSILON && high - mid > DBL_EPSILON)  // 精度
	{
		if ((mid * mid - x) * (low * low - x) < 0)
			high = mid;
		else low = mid;
		mid = (high + low) / 2;
	}
	return int(mid);
}

注:

初始上界为x + 0.25,而非x。(这一点我不是很懂,希望大佬指点)

牛顿迭代法

看概念:
又称为牛顿-拉弗森方法(英语:Newton-Raphson method),它是一种在实数域和复数域上近似求解方程的方法。方法使用函数f(x)f(x)的泰勒级数的前面几项来寻找方程 f(y)=0f(y) = 0的根。
可以看出满足上面的题目分析,可以求得根。

  • 原理步骤
    选择一个接近函数f(x)f(x)零点的 x0x_0,计算相应的f(x0)f(x_0)和切线斜率 f(x0)f&#x27;(x_0)(这里ff&#x27;表示函数 ff的导数)。然后我们计算穿过点 f(x0)f(x_0)并且斜率为f(x0)f&#x27;(x_0)的直线和xx轴的交点的x坐标,也就是求如下方程的解:
    0=(xx0)f(x0)+f(x0)0 = (x - x_0) * f&#x27;(x_0) + f(x_0)
    我们将新求得的点的xx坐标命名为 x1x_1,通常x1x_1会比 x0x_0更接近方程 f(x)=0f(x)=0的解。因此我们现在可以利用x1x_1开始下一轮迭代。迭代公式可化简为如下所示:
    xn+1=xnf(xn)f(xn)x_{n+1} = x_n - \frac{f(x_n)}{f&#x27;(x_n)}
  • 图示
    sqrt函数实现——二分法、牛顿迭代法
    将上述的迭代式进行化简:
    因为要求的是开平方的解,所以f(xn)f&#x27;(x_n)2xn2x_n,同时将f(xn)=xn2Nf(x_n) =x_n^2 - N带入公式xn+1=xnf(xn)f(xn)x_{n+1} = x_n - \frac{f(x_n)}{f&#x27;(x_n)}中,可以化简得到:xn+1=(xn+N/xn)/2x_{n+1} = (x_n + N/x_n) / 2
  • 代码实现
int NewtonSqrt(int x) {
	if (x == 0) return 0;
	double result = 1, pre = 0;
	while (result != pre)
	{
		pre = result;
		result = (result + x / result) / 2;
	}
	return int(result);
}

通过查阅的资料,以及在leetcode上的实际提交,发现牛顿迭代法的效率要比二分法高。
具体的效率差异请参考:https://blog.csdn.net/xusiwei1236/article/details/25657611