问题
给定一个连续单变量函数\(f(x)\),求这个函数的零点\(x_0\)。要求可控制误差。
解决方案
二分法与牛顿法都是适合计算机的解决方案。不过,牛顿法远快于二分法,写起来也更简单,但是更难理解。
二分法
算法是这样的:
- 找出(不管用什么方法,甚至看图像也行)两个值:\(l\)(low)与\(h\)(high)。使得\(f(l)<0\)且\(f(h)>0\)。由于连续性,\(x_0\)在\(l\)与\(h\)之间。
- 设\(m=\frac{l+h}{2}\),判断\(f(m)\)的正负性:
\(f(m)>0 \Leftrightarrow x_0在l与m之间\)
\(f(m)=0 \Leftrightarrow x_0=m\)
\(f(m)<0 \Leftrightarrow x_0在m与h之间\)
以上便是一次迭代。每经过一次迭代,\(x_0\)的范围就会缩小一半。于是,经过多次迭代,\(l \mapsto h\),我们就能将\(l\)或\(h\)作为\(x_0\)的近似值。
下面用二分法求函数\(f(x)=3^{x+1}+x^3\)的零点:
- 输入函数;画图
由函数图像可以猜测\(l=-10\)与\(h=0\) - 写个程序进行迭代(误差设定为\(10^{-5}\))
花了二十次。
牛顿法
牛顿法的算法是这样的:
假设\(x\)是猜测的\(x_0\)值,且\(x_1=x-\frac{f(x)}{f \prime (x)}\),那么\(x_1\)比\(x\)更接近\(x_0\)。
以我现有的知识,无法理解牛顿法。但写个程序还是可以的:
- 我们已经画好了图,由图像可以看到\(0\)比\(-10\)更接近\(x_0\),所以我们猜测\(0 \mapsto x\)
- 进行迭代(误差一样,设定为\(10^{-5}\))
可以看到,牛顿法的代码更精简,而且更快:只需要5次。
结语
牛顿法效率远高于二分法。但是牛顿法公式是怎么求出来的呢?希望在以后的学习中能够了解。
最后,附上两段Eigenmath代码,是经过排版与添加注释的二分法与牛顿法演示代码:
已经在Eigenmath 137上测试通过。
二分法
f(x) = 3^(x+1)+x^3
draw(f)
l=-10 # low
h=0 # high
cnt=0 # count, 记录迭代次数
error=10^(-5) # 误差。若abs(high - low)<error则输出结果
for(k,1,10^9, # 无限循环,直到小于误差
do(
cnt=cnt+1, # 记录迭代次数
m=(l+h)/2, # 取二分点m
test(
f(m)>0, # 若f(m)>0
h=m, # h=m
l=m # 否则 l=m
),
test(
abs(h-l) > error, # 若结果大于误差
1, # 什么都不做,继续迭代
do( # 否则
print(
float(l), # 输出low(小数形式)
float(h), # 输出high(小数形式)
cnt # 输出迭代次数
),
stop # 退出迭代
)
)
)
)
牛顿法
f(x) = 3^(x+1)+x^3
draw(f)
guess=0 # 猜测的x0的值
cnt=0 # 记录迭代次数(同上)
error=10^(-5) # 误差(同上)
for(k,1,10^9, # 无限循环(同上)
do(
cnt=cnt+1, # 记录迭代次数(同上)
x1=float(guess- f(guess)/eval(d(f,x),x,guess)), # 使用牛顿法公式得出一个比guess更接近x0的值
test( abs(x1-guess) > error, # 若猜测值增量大于误差(表示还没猜到)
guess=x1, # 更新猜测值guess,继续迭代
do( # 否则
print(x1, cnt), # 输出接近x0的猜测值与迭代次数
stop # 退出迭代
)
)
)
)