问题

给定一个连续单变量函数\(f(x)\),求这个函数的零点\(x_0\)。要求可控制误差。

解决方案

二分法与牛顿法都是适合计算机的解决方案。不过,牛顿法远快于二分法,写起来也更简单,但是更难理解。

二分法

算法是这样的:

  1. 找出(不管用什么方法,甚至看图像也行)两个值:\(l\)(low)与\(h\)(high)。使得\(f(l)<0\)且\(f(h)>0\)。由于连续性,\(x_0\)在\(l\)与\(h\)之间。
  2. 设\(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\)的零点:

  1. 输入函数;画图

    由函数图像可以猜测\(l=-10\)与\(h=0\)
  2. 写个程序进行迭代(误差设定为\(10^{-5}\))

    花了二十次。

牛顿法

牛顿法的算法是这样的:

假设\(x\)是猜测的\(x_0\)值,且\(x_1=x-\frac{f(x)}{f \prime (x)}\),那么\(x_1\)比\(x\)更接近\(x_0\)。

以我现有的知识,无法理解牛顿法。但写个程序还是可以的:

  1. 我们已经画好了图,由图像可以看到\(0\)比\(-10\)更接近\(x_0\),所以我们猜测\(0 \mapsto x\)
  2. 进行迭代(误差一样,设定为\(10^{-5}\))
  3. 二分法求函数零点java 二分法求函数零点公式_二分法求函数零点java


  4. 可以看到,牛顿法的代码更精简,而且更快:只需要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                                            #   退出迭代
            )
        )
    )
)