牛顿迭代法及其应用

一. 概述

五次及以上多项式方程没有根式解(就是没有像二次方程那样的万能公式),这个是被伽罗瓦用群论做出的最著名的结论。 那么这样的方程该如何求根呢???

牛顿于 1736 年公开提出了 牛顿迭代法来解决这个问题.
核心思想“逼近”.

二. 几何意义

我们知道,求一个方程的根,等同于求一个方程与x轴的交点的横坐标.
在这里插入图片描述

如何求交点的横坐标呢???

根据上图我们可以观察到:
从 x0 到 x1 到 x2, 它们离交点的横坐标越来越近了!!!
其最终会无限接近 交点的横坐标!!

迭代过程:

  1. 任选一点 x 0 x_0 x0 作垂线与 f(x) 交于 ( x 0 , f ( x 0 ) ) (x_0, f(x_0)) (x0,f(x0))
  2. ( x 0 , f ( x 0 ) ) (x_0, f(x_0)) (x0,f(x0)) 作 f(x)图像的切线, 与 x 轴交于 x 1 x_1 x1
  3. 以 x1 为新点做垂线… 重复 1,2,3 步骤,直到得到满意的近似解 x n x_n xn

 以上就是牛顿迭代法的全部过程了.

推论:
 => 斜率 = f ′ ( x 0 ) = f ( x 0 ) x 0 − x 1 f'(x0) = \frac{f(x0)}{x0-x1} f(x0)=x0x1f(x0)
 => x 1 = x 0 − f ( x 0 ) f ′ ( x 0 ) x1 = x0 - \frac{f(x0)}{f'(x0)} x1=x0f(x0)f(x0)
 => x n + 1 = x n − f ( x n ) f ′ ( x n ) x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} xn+1=xnf(xn)f(xn)

三. 应用

3.1 求函数的根

def Dfunc(function, guess_value, delta=1e-5):
    '''
    通过导数的定义式求f(guess_value)这点的导数
    '''
    return (function(guess_value+delta)-function(guess_value))/delta

def newton_update(function, guess_value):
    '''
    通过迭代公式算出新的值
    '''
    return guess_value - function(guess_value)/Dfunc(function,guess_value)

def is_close_root(function,guess_value):
    '''
    判断是否已接近我们要求的根
    '''
    return function(guess_value) == 0  #精度要求过高可能会造成迭代次数过多
    #return function(guess_value) < 1e-8  #折中方案

def newton_func(function, guess_value=2):
    while not is_close_root(function,guess_value):
        guess_value = newton_update(function, guess_value)
    return guess_value

def find_root(function):
    '''
    lambda 作为函数传入,求函数的根
    '''
    return newton_func(function)

print(find_root(lambda x:pow(x,2)-9))#传入的是函数
print(find_root(lambda x:pow(x,2)-16))
print(find_root(lambda x:pow(x,3)-27))
print(find_root(lambda x:pow(x,3)-8))

3.2 开n次方: k a k\sqrt{a} ka

要求 1 2 256 \frac{1}{2}\sqrt{256} 21256  即解方程 x 2 = 256 x^2=256 x2=256, 即求 x 2 − 256 = 0 x^2-256=0 x2256=0 的根(移项即可).
推广为: k a k\sqrt{a} ka ------> 解 x 1 k = a x^{\frac{1}{k}}=a xk1=a ------> 求 y = x 1 k − a 的 根 y=x^{\frac{1}{k}}-a的根 y=xk1

#求根只需要调用之前写的 求根函数即可
print(find_root(lambda x:pow(x,2)-256))
print(find_root(lambda x:pow(x,3)-27))
print(find_root(lambda x:pow(x,2)-2))
3.21 优化: 牛顿迭代方程的 化简

迭代方程为:
 => x n + 1 = x n − f ( x n ) f ′ ( x n ) x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} xn+1=xnf(xn)f(xn)
f ( x n ) = x 2 − a 时 f(x_n) = x^2 -a 时 f(xn)=x2a
 => x n + 1 = x n − x n 2 + a 2 x n x_{n+1} = x_n - \frac{x_n^2+a}{2x_n} xn+1=xn2xnxn2+a
 => x n + 1 = 2 x n 2 − x n 2 + a 2 x n x_{n+1} = \frac{2x_n^2-x_n^2 +a}{2x_n} xn+1=2xn2xn2xn2+a
 => x n + 1 = x n 2 + a 2 x n x_{n+1} = \frac{x_n^2+a}{2x_n} xn+1=2xnxn2+a
 => x n + 1 = ( x n 2 + a x n ) / 2 x_{n+1} = (\frac{x_n^2+a}{x_n})/2 xn+1=(xnxn2+a)/2
 => x n + 1 = ( x n + a x n ) / 2 x_{n+1} = (x_n + \frac{a}{x_n})/2 xn+1=(xn+xna)/2
 对 f ( x ) = x n − a f(x) = x^n -a f(x)=xna 这样的函数的牛顿迭代求根公式可以化简为:
x n + 1 = ( n − 1 ) x n + a p o w ( x n , n − 1 ) n x_{n+1} = \frac{(n-1)x_n+\frac{a}{pow(x_n, n-1)}}{n} xn+1=n(n1)xn+pow(xn,n1)a
化简后的迭代公式,更快!

#使用优化后迭代公式的代码,仅仅针对于 f(x) = x^n -a
def newton_update(base, expoent, guess_value):
    return ((expoent-1)*guess_value + base/pow(guess_value, expoent-1))/expoent

def is_close_root(base, expoent, guess_value):
    return abs(pow(guess_value, expoent) - base ) < 1e-12

def newton_func(base, expoent, guess_value):
    while not is_close_root(base, expoent, guess_value):
        guess_value = newton_update(base, expoent, guess_value)
    return guess_value

def find_x_n_a(base, expoent, guess_value=2):
    '''
    求 y = x^n -a 的根
    '''
    return newton_func(base, expoent, guess_value)

def sqrt(base):
    return find_x_n_a(base, 2, (base+1)/2)

print(find_x_n_a(256, 2))
print(find_x_n_a(2, 2))
print(find_x_n_a(8, 2))
print(find_x_n_a(8, 3))

cpp:

#include <iostream>
#include <cmath>
using namespace std;


double find_x_n_a(double base, double expoent, double guess_value=2){
  return abs(pow(guess_value, expoent)-base)<1e-10 ? guess_value
    : find_x_n_a(base, expoent,
        ((expoent-1)*guess_value + base/pow(guess_value, expoent-1))/expoent);
}

int main(){
  cout << find_x_n_a(9,2) << endl;
  cout << find_x_n_a(27,3) << endl;
  cout << find_x_n_a(8,2) << endl;
  cout << find_x_n_a(2,2) << endl;
  return 0;
}

3.3 函数pow(base, expoent)

base^expoent 即 y = a x y = a^x y=ax 这个函数的求值操作
x为正数:
 x 为整数,可以用 快速幂算法 求解
 x 为小/分数,实际上是 开方操作
x 为负数:
 对正数的结果 求倒数 即可. 或者: 求 ( 1 b a s e ) − x 求(\frac{1}{base})^{-x} (base1)x 次方(更快)

开方幂函数指数函数
1 2 a \frac{1}{2}\sqrt{a} 21a a 1 2 a^{\frac{1}{2}} a21 y = x 2 − a 求 根 y = x^2-a求根 y=x2a
1 3 a \frac{1}{3}\sqrt{a} 31a a 1 3 a^{\frac{1}{3}} a31 y = x 3 − a 求 根 y = x^3 -a 求根 y=x3a
2 3 a \frac{2}{3}\sqrt{a} 32a a 2 3 a^{\frac{2}{3}} a32 y = x 3 2 − a 求 根 y = x^{\frac{3}{2}} -a 求根 y=x23a
1 2 3 a \frac{1}{\frac{2}{3}\sqrt{a}} 32a 1 a − 2 3 a^{-\frac{2}{3}} a32 y = x 3 2 − a 的 正 根 的 倒 数 y = x^\frac{3}{2} - a 的正根的倒数 y=x23a
  1. a 2 \frac{a}{2} 2a 为起始点, 其垂线交于 ( a 2 , f ( a 2 ) ) (\frac{a}{2}, f(\frac{a}{2})) (2a,f(2a))
  2. 求此点处的导数
  3. 用之前的推论算出起始点的下一个点 x n e x t x_{next} xnext
  4. 迭代多次得出近似解 x n x_n xn

写成 python 版本即为:


def newton_update(base, expoent, guess_value):
    return ((expoent-1)*guess_value + base/pow(guess_value, expoent-1))/expoent

def is_close_root(base, expoent, guess_value):
    return abs(pow(guess_value, expoent) - base ) < 1e-13

def newton_func(base, expoent, guess_value):
    while not is_close_root(base, expoent, guess_value):
        guess_value = newton_update(base, expoent, guess_value)
    return guess_value

def find_x_n_a(base, expoent, guess_value=2):
    '''
    求 y = x^n -a 的根
    '''
    return newton_func(base, expoent, guess_value)


def int_quick_pow(base, expoent):
    result = 1
    while expoent:
        if expoent & 1:
            result *= base
        expoent >>= 1
        base *= base
    return result

def pow_h(base, expoent):
    '''
    y = a^x  y = base^expoent
    '''
    if expoent < 0:
        '''
        a^-p = (1/a)^p 或  y = x^(1/expoent)-a
        '''
        return pow_h(1/base, -expoent)# a(-2/3) = 1/(a^(2/3)) = (1/a)^(2/3)
    elif expoent == 0:
        return base

    elif not expoent %1:
        return int_quick_pow(base, expoent)

    return find_x_n_a(base, 1/expoent, (base+1)/1) # y = x^(1/expoent) - a

    '''
    1 收敛缓慢或者不收敛 用 牛顿迭代法
    '''
    '''
    小数倍 => 1.用泰勒级数
    2.如果在 cpp 等静态类型语言中,由于 (base, fenzi, femu), n/m根号a 可以使用 m根号a^n 来计算
    '''


print(pow_h(3,0))
print(pow_h(3,-1))

print(pow_h(4,-2))

print(pow_h(8,1/3))
print(pow_h(8,-1/3))

3.32 泰勒级数优化

快速收敛的幂级数要快于牛顿法
未完待续

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值