在LeetCode上面有一道Easy题,是sqrt(int x),即求整数x的平方根。目前比较通用的一个方法就是采用牛顿迭代法来求平方根,它又被称为牛顿- 拉弗森方法,该方法主要思想就是 切线是曲线的线性逼近 。本文大致结构分为两部分,第一部分阐述牛顿迭代法的数学原理,第二部分简单说明如何在求平方根这一问题上应用牛顿迭代法,最后是一个简单总结。
牛顿迭代法
多数方程不存在求根公式,因此求精确根非常困难,甚至不可能,从而寻找方程的近似根就显得特别重要。
几何-代数推导
已知曲线,在点作切线,交轴于点,接着在点作切线交轴于点,图像如下:
显然,点处的切线方程为,所以容易得到: ,求出点的值后,和上面一样的步骤,可以得到过点的切线方程,于是又可以得到点的值,这样不断迭代下去,直至无限逼近零点(即曲线与轴的交点)。
这种方法直觉上是对的,但是这里缺乏严格的数学证明,但这个其实已经被证明,来看看收敛的充分条件:
若二阶可导,那么在待求的零点周围存在一个区域,只要起始点位于这个邻近区间内,那么牛顿-拉弗森方法必定收敛。
和泰勒级数的关系
用牛顿迭代法解非线性方程,是把非线性方程线性化的一种近似方法。为什么这么说呢?这就要提起泰勒级数了(泰勒大法好啊,这辈子都会记住,毕竟在这儿栽过跟头),我们把在点的某邻域内展开成泰勒级数:
取其线性部分,也就是泰勒展开的前两项,并令其等于0,即,以此作为非线性方程的近似方程,若一阶导数存在,且不为零(因为要做分母),则其解为,于是我们便得到了关于牛顿-拉弗森的一个迭代关系式:
可以发现,这个迭代公式和我们之前通过几何图推导出来的完全一致,这里要注意的是最后求出的零点并不是精确值,而是一个近似解,但是我们可以无限逼近精确值,这和极限的思想一个道理。
求平方根
前面简单的讲了牛顿迭代法的数学原理,如果有兴趣,可以点这里去仔细阅读关于牛顿迭代法更深层的本质,这里不再赘述。
有了这个利器,求平方根就非常简单了,高次方程的根其实也可以借此法求解(但是对函数特性有要求,二次函数可以收敛,这里不必担心)。假如我们要求,那怎么做呢?
步骤:
- 令,则有方程;
- 令,显然我们要求的就是曲线与轴的交点;
- 随便给一个初值,根据这一迭代公式,可以计算出,…,那计算到什么时候停止呢?看下面:
- 计算的增量的绝对值,即,根据题目要求的精度,我们可以设定当时便停止迭代,计算中止;
- 此时得到的便为方程的根,即.
程序的实现
有了上述的基本分析后,就很容易写出代码了,采用c++编写如下,当然这里贴出的代码是对整数求平方根,不过对浮点数此法亦是可行的,只需更改一下精度即可。
|
|
效率
在采用牛顿迭代法之前,我用一个类似于二分的思想撸了一个求平方根的方法(仅适用于正整数),运行效率还是相当的差,在这里也贴出比较愚的代码。
|
|
两种方法的运行效率相差很大,在同一测试集上跑,牛顿迭代法运行时间是3ms,二分思想(暂且就这么叫吧)是45ms,显然牛顿迭代法比二分思想的效率高达15倍,真是不知道比它高到哪里去了^_^。
贴一张关于两种方法的效率对比图
总结
本文先简要阐述了牛顿迭代法的原理,再将其和泰勒级数的关系理了一下,最后着重说了一下如何利用牛顿迭代法求解平方根。当然,牛顿迭代法的用途主要是用来求解非线性方程的,而平方根只是其中一个特例,比如三次方、四次方程等等都可以用来求解。