算法-牛顿迭代法
前置知识
- 函数(数学意义上的)
- 导数(斜率)
- 基本导函数
泰勒展开(不需要)- 小学几何(雾)
思路
现在,你有一个方程
f
(
x
)
=
0
f(x)=0
f(x)=0,怎么求解它的根(近似根)呢?
牛顿是这么干的(以下以方程
x
3
+
2
x
2
+
3
x
+
4
=
0
为例
x^3+2x^2+3x+4=0为例
x3+2x2+3x+4=0为例):
先作函数
f
(
x
)
f(x)
f(x) 的图像,并任取在
x
x
x 轴上的一点
A
A
A
作
f
(
x
)
f(x)
f(x) 在A处的切线
g
g
g,
g
g
g与
x
x
x 轴交于
B
B
B(本人很菜,此处表述不太准确,请结合图片理解)
重复上述操作,就可以得到方程
f
(
x
)
=
0
f(x)=0
f(x)=0 其中一个近似根了。
这是迭代
4
4
4 次的效果
这里的点
E
E
E 已经极其精确了,误差在
1
0
−
5
10^{-5}
10−5 左右。
我们可以根据下文的递推式证明牛顿迭代法的近似根精度是以平方的级别增长的。
(图片来源:GeoGebra)
数学实现
接下来,考虑怎么使用数学的语言求出递推式。
这里给出不使用泰勒展开的证明(好像很简单啊)
我们设迭代
k
k
k 次后得到的近似根为
x
k
x_k
xk
此时,在
x
x
x 轴上
x
k
−
x
k
+
1
=
f
(
x
k
)
f
′
(
x
k
)
x_k-x_{k+1}=\frac{f(x_k)}{f'(x_k)}
xk−xk+1=f′(xk)f(xk)(请自证)
将
x
k
+
1
x_{k+1}
xk+1 用
x
k
x_k
xk 表示,则有
x
k
+
1
=
x
k
−
f
(
x
k
)
f
′
(
x
k
)
x_{k+1}=x_k-\frac{f(x_k)}{f'(x_k)}
xk+1=xk−f′(xk)f(xk)
这就是牛顿迭代法的递推式。
实现代码
这是自己写的奇奇怪怪的程序的一部分。。。
此程序用来处理整次数多项式方程的根。
void NewtonIterationMethod(){//f(x)=0
ll k;long double a[6],x;
cout<<"This code will help you solve f(x)=0(decimal solution)!\n";
cout<<"Please type in the maxium power times k:";cin>>k;
cout<<"please type in k+1 numbers, which is the coefficient of x^0 to x^k:";for (ll i=0;i<=k;i++) cin>>a[i];
cout<<"Please type in the initial solution x0:";cin>>x;
cout<<"We can use Newton iteration method to solve the equation\n";
for (ll i=1;;i++){
printf("We use the folmula x%d=x%d-f(x%d)/f'(x%d) to calculate x%d\n",i,i-1,i-1,i-1,i);
long double f1=0.000,f2=0.000;
for (ll i=0;i<=k;i++) f1=f1+a[i]*pow(x,i);
for (ll i=1;i<=k;i++) f2=f2+i*a[i]*pow(x,i-1);
assert(fabs(f2)>eps);
long double xp=x-f1/f2;
printf("So, we can get that x%d=%.18Lf\n",i,xp);
x=xp;
ll f;
cout<<"Do you want another calculation?\ntype 1 for yes, or type any other for no:";cin>>f;
if (f!=1) break;
}
printf("So the solution we get is: x=%.18Lf\n",x);
}
练习
- Leetcode69 x 的平方根
- 洛谷B3627 立方根
- UVA10428 The Roots 原题链接
这才是牛顿迭代法该用的地方
附记
牛顿迭代法在处理某些方程时会出现迭代不收敛的情况。
例子:方程
x
=
0
\sqrt{x}=0
x=0 的递推式为
x
k
+
1
=
−
x
k
x_{k+1}=-x_k
xk+1=−xk 一下子无语了。
此时,建议使用改进的牛顿迭代法解决:下山法改进
我才不会告诉你我不会其他方法呢