两种方法对浮点数求根号(二分法和牛顿法)

二分法和牛顿法求根号是面试中的经典题,如果没提前接触过,经典题将成为经典难题。我先上代码,后面再对代码进行解释:

#include<iostream>
#include<string>
#define PRECISION 0.0002
using namespace std;

//二分法
//二分法通过缩小根值范围的方法来逼近结果
float sqrt1(float n) {
	float min, max, mid; //min代表下边界,max代表上边界,mid为中间值也作为近似值
	min = 0;
	max = n;
	mid = n / 2;
	while (mid*mid>n + PRECISION || mid*mid<n - PRECISION)
	{
		mid = (max + min) / 2;
		if (mid*mid < n + PRECISION) {
			min = mid; //根值偏小,升高下边界
		};
		if (mid*mid > n - PRECISION) {
			max = mid;//根值偏大,降低上边界
		}
	}
	return mid;
}

//牛顿法
float sqrt2(float n) {
	float k = n;
	while (1) {
		if (k*k > n - PRECISION && k*k < n + PRECISION) {
			break;
		}
		k = 0.5*(k + n/k);//通过牛顿法得出
	}
	return k;
}
int main() {
	float a = 11.283;
	float res1, res2;
	res1 = sqrt1(a);
	res2 = sqrt2(a);
	cout << "num is "<< a << endl;
	cout << "二分法结果: " << res1 << endl;
	cout <<"牛顿法结果: "<< res2 << endl; 
	cout << "二分法验证: " << res1 * res1 << endl;
	cout <<"牛顿法验证: "<<res2 * res2 << endl;
	system("pause");
	return 0;
}

对于二分法,看注释就可以看得很明白了。对于牛顿法,有着更简洁的代码,但需要花一点数学思维来理解。其实,直接看牛顿法会对这道题的理解更费解,因为牛顿法的目标并不是为了开根号。 很多博主也不太负责任的直接贴上牛顿法的证明方法,对于读者理解这道题来说反而有误导作用。

牛顿法的目的是求方程的近似解,即函数曲线与横坐标的交点。比如,f(x)=x^5+2x^2+8,求f(x)=0时,x的值。

那么这个牛顿法跟开根号有什么关系呢,f(x)=\sqrt x可以转换一个思路,x = y^2 ==> y^2 - x = 0

还有点难理解对吧,对于求开方,我们可以确定知道x的值,比如x=67,y=?。

那么上述公式就等价于f(y) = y^2 - 67, 求f(y)=0时,y的取值。这样一来就可以转换成牛顿法来解决。我们可以画出f(y)的图像,其实就是f(y)=y^2标准抛物线向下平移67个单位的样子:

上述图像就是x=67时的转换图像,我们要求图像和x轴正半轴的交点(根号值只可能为正),即上图标出的y点。

首先,在x轴右半轴上任意取一点p,p点作垂线求得与曲线的交点,即f(p),如上述黑线所示,p点未标出(就是y点左边那个交点)。

求出f(p)之后,再对点(p, f(p))作一条切线,这条切线务必与x轴有一个交点(这个交点比p更接近y)。

我们可以用同样的方法对这个交点操作一遍(如红线所示),那么新交点一定会更接近y。取最后一个epoch的取值当作y的近似值。

那么,就可以建立一种数学联系。

设第一个点为(p1,0),则其垂线与曲线交点为f(p1),则切点为(p1, f(p1)),切线斜率为f '(p1),知道斜率和一个确定点,就可以确定这条直线(切线),那么自然可以求得这条切线与x轴的交点(p2,0)。以此来确定,p2和p1之间的对应关系,这种对应关系可以泛化到p_{n+1}对pn的对应关系。

可以简单推一推:

上面得出p_{n+1}p_n更接近y值的结论,所以我们只需把p的下标增大点就可以无限接近y了。

带入到原式子可以得到,k = 0.5*(k + n/k)

k代表的就是p,n代表的就是x。

猜你喜欢

转载自blog.csdn.net/leviopku/article/details/82811478
今日推荐