Python代码中的数学之美:用牛顿逼近法计算2的算术平方根

举报
天元浪子 发表于 2021/07/26 22:39:52 2021/07/26
【摘要】 编程的核心是算法,而算法的核心是数学——单从这一点上讲,编程与数学可谓关系密切。编程所需要的很多能力和数学是相通的,比如逻辑思维、抽象能力等。编程可以帮助程序员更好地理解数学,将复杂的数学可视化,也可以作为解决数学问题的工具,更能强化数学能力。本文作为一个系列的开篇之作,尝试站在程序员的角度,用程序员的方式去理解数学王国的经典概念。 如果有人问2的算术平方根...

编程的核心是算法,而算法的核心是数学——单从这一点上讲,编程与数学可谓关系密切。编程所需要的很多能力和数学是相通的,比如逻辑思维、抽象能力等。编程可以帮助程序员更好地理解数学,将复杂的数学可视化,也可以作为解决数学问题的工具,更能强化数学能力。本文作为一个系列的开篇之作,尝试站在程序员的角度,用程序员的方式去理解数学王国的经典概念。

如果有人问2的算术平方根是多少,相信所有的程序员都会张口说出1.414这个答案。不过,要是精度要求更高一点,大多数的程序员就只能依赖计算器了。比如,Python程序员会这样写:

>>> pow(2, 1/2)
1.4142135623730951

  
 
  • 1
  • 2

或者,像下面这样写,也没有问题:

>>> 2**(1/2)
1.4142135623730951

  
 
  • 1
  • 2

但是,如果想要通过计算的方式(不是指在草纸上手工开方),得到高精度的结果,作为程序员应该如何写代码呢?这里,以计算2的算术平方根为例,介绍科学巨人牛顿的逼近法。

牛顿逼近法是牛顿在17世纪提出的一种在实数域和复数域上近似求解方程的方法,虽然证明起来有点麻烦,但原理很简单,实用性很强。假设有方程:
f ( x ) = 0 f(x)=0 f(x)=0

首先,选择一个接近函数 f ( x ) f(x) f(x)零点的 x 0 x_0 x0,计算对应的 f ( x 0 ) f(x_0) f(x0)和切线斜率 f ′ ( x 0 ) f^{'}(x_0) f(x0)(这里 f ′ f^{'} f表示函数 f f f
的导函数);然后计算函数 f ( x ) f(x) f(x)在点 ( x 0 , f ( x 0 ) ) (x_0,f(x_0)) (x0,f(x0))的切线(斜率为 f ′ ( x 0 ) f^{'}(x_0) f(x0))和 x x x轴的交点的 x x x坐标,也就是求如下方程的解:
( x − x 0 ) f ′ ( x 0 ) + f ( x 0 ) = 0 (x-x_0)f^{'}(x_0) + f(x_0) = 0 (xx0)f(x0)+f(x0)=0
x = x 0 − f ( x 0 ) f ′ ( x 0 ) x = x_0 - \frac{f(x_0)}{f^{'}(x_0)} x=x0f(x0)f(x0)

将新求得的点的 x x x坐标命名为 x 1 x_1 x1,通常 x 1 x_1 x1会比 x 0 x_0 x0更接近方程 f ( x ) = 0 f(x)=0 f(x)=0的解。因此可以利用 x 1 x_1 x1开始下一轮迭代。迭代公式可化简为如下所示:
x n = x n − 1 − f ( x n − 1 ) f ′ ( x n − 1 ) x_n = x_{n-1} - \frac{f(x_{n-1})}{f^{'}(x_{n-1})} xn=xn1f(xn1)f(xn1)

经过 n n n次迭代,当 f ( x n ) f(x_n) f(xn)的绝对值满足精度要求时,即可认为 x n x_n xn就是方程 f ( x ) = 0 f(x)=0 f(x)=0的近似解。

在这里插入图片描述
理解了牛顿迭代法,就不难写出计算2的算术平方根的代码。

def f(x): # 定义函数f(x) return x**2 - 2

def df(x): # 定义函数f(x)的导函数 return 2*x

def newton_raphson_method(): # 牛顿-拉弗森方法 tiny = 1e-15 # 当f(x_n)小于tiny时,迭代结束 i, x = 0, 2 # 迭代计数器和初始值 while True: i += 1 # 迭代计数器加1 x = x-f(x)/df(x) # 计算下一个x if abs(f(x)) < tiny: # 满足迭代结束条件 print('\n经过%d次迭代,2的算术平方根为%.030f'%(i, x)) break

if __name__ == '__main__': newton_raphson_method()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

运行结果如下:

经过5次迭代,2的算术平方根为1.414213562373095145474621858739

请按任意键继续. . .

  
 
  • 1
  • 2
  • 3

这个结果和使用pow函数计算出来的结果,小数部分的前30位完全一致。

>>> '%.030f'%pow(2,1/2)
'1.414213562373095145474621858739'

  
 
  • 1
  • 2

最后,再介绍一个计算函数导数的通用方法。对于类似 x 2 − 2 x^2-2 x22这样的函数,我们当然可以直接写出导函数 2 x 2x 2x。但如果函数足够复杂,不能直接写出导函数,则可以象下面这样使用微分法近似计算函数在 x = x i x=x_i x=xi处的导数。

def df(xi): # 返回函数f(x)在xi处的导数 delta = 1e-15 return (f(xi+delta)-f(xi-delta))/(2*delta)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

使用这个近似的求导函数,迭代得到的结果就会有一点误差,小数部分的前15位和上面两种方法一致。

经过15次迭代,2的算术平方根为1.414213562373095367519226783770

请按任意键继续. . .

  
 
  • 1
  • 2
  • 3

文章来源: xufive.blog.csdn.net,作者:天元浪子,版权归原作者所有,如需转载,请联系作者。

原文链接:xufive.blog.csdn.net/article/details/116448907

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。