如何通过牛顿法求近似平方根?

这是一篇考拉内部小型技术分享的文章。
这次分享一个求近似平方根的快速方法: 牛顿法。
先上代码:

def sqrt(n):
    ret = n
    while ret * ret > n:
        ret = (ret + n / ret) / 2
    return ret

print(sqrt(4))
print(sqrt(2))
复制代码

代码很简短,很神奇,为什么这样子可以求出来平方根呢?下面来推导一下。

设n的平方根为x, 则有 x^2 = n, 即x^2 - n = 0, 写成对x的函数的形式为f(x) = x^2 - n。假设n=4, 我们都知道,4的平方根是2,那用牛顿法怎么求出来呢?先画出来这个函数的图形。

from matplotlib import pyplot as plt
import numpy as np
%matplotlib notebook

xs = np.linspace(-6, 6, 1000)
ys = [x * x - 4 for x in xs]

plt.xlabel('x')
plt.ylabel('y')
plt.plot(xs, [0] * 1000)
plt.plot([0] * 1000, np.linspace(-6, 30, 1000))
plt.plot(xs, ys)
复制代码
<IPython.core.display.Javascript object>  
复制代码

[<matplotlib.lines.Line2D at 0x1217b25f8>]
复制代码

然后我们取一个点,先取点x_0(4, 12), 然后做一条切线,它会跟x轴相交于点(2.5, 0), 相同横坐标对应函数上的点为x_1(2.5, 2.25), 然后我们在x_1处再做一条切线,它会和x轴相交于点(2.05, 0), 相同横坐标对应函数上的点为x_2(2.05, 0.2025), 继续这样迭代下去,将很快求出来最后x是2.

def f(x):
    return x * x - 4

xs = np.linspace(-6, 6, 1000)
ys = [f(x) for x in xs]

plt.xlabel('x')
plt.ylabel('y')
plt.plot(xs, [0] * 1000)
plt.plot([0] * 1000, np.linspace(-6, 30, 1000))
plt.plot(xs, ys)
plt.plot(4, f(4), 'ro')
plt.annotate('x0(4, 12)', (2, 12))
plt.plot([4, 4], [0, 12], '--')

k0 = (f(4 + 0.1) - f(4 - 0.1)) / 0.2
b0 = f(4) - k0 * 4

def f_tangent0(x):
    """
    点x0的切线方程
    """
    return k0 * x + b0

xs = np.linspace(2, 6, 1000)
ys = [f_tangent0(x) for x in xs]
plt.plot(xs, ys)

plt.plot(2.5, f(2.5), 'ro')
plt.annotate('x1(2.5, 2.25)', (0.5, 5))
plt.plot([2.5, 2.5], [0, 2.25], '--')

k1 = (f(2.5 + 0.1) - f(2.5 - 0.1)) / 0.2
b1 = f(2.5) - k1 * 2.5

def f_tangent1(x):
    """
    点x1的切线方程
    """
    return k1 * x + b1

xs = np.linspace(1, 6, 1000)
ys = [f_tangent1(x) for x in xs]
plt.plot(xs, ys)


# plt.plot(2.05, f(2.05), 'ro')
# plt.annotate('x1(2.05, 0.2)', (2.05, -5))

复制代码
<IPython.core.display.Javascript object>
复制代码

[<matplotlib.lines.Line2D at 0x12ee97c18>]
复制代码

从图形上可以比较直观的理解牛顿迭代法,但是从代数上怎么进行计算呢?现在来推导一下:

设n的平方根为x, 则有 x^2 = n, 即x^2 - n = 0, 写成对x的函数的形式为f(x) = x^2 - n,我们取一个点x_0, 作一条切线,那么切线的斜率k就是f(x) = x^2 - n的导数:

t = f\prime(x) = 2x

由上面的图可以看出来,作x0到x轴的垂线,围成了一个三角形,由三角定理可知:

t = \dfrac{f(x_0)}{x_0 - x_1} = \dfrac{x_0^2 - n}{x_0 - x_1}

所以有:

2x_0 = \dfrac{x_0^2 - n}{x_0 - x_1}

化简得:

2x_0(x_0 - x_1) = x_0^2 - n
2x_0^2 - 2x_0x_1 = x_0^2 - n
x_0^2 + n = 2x_0x_1
x_1 = \dfrac{x_0 + \dfrac{n}{x_0}}{2}

再看一次代码:

def sqrt(n):
    ret = n
    while ret * ret > n:
        ret = (ret + n / ret) / 2
    return ret
复制代码

一致!

牛顿迭代法求平方根就是这样推导出来的。

其实牛顿法,除了应用在求平方根上,还有很多应用,在机器学习算法的最后优化步骤中,会使用牛顿法求任意函数的最优解,不仅限于x^2 -n这种类型。

建议大家做一下leetcode这道题: sqrtx,会加深理解。


分享内容出自考拉程序猿Hank的blog Hank‘s blog

转载于:https://juejin.im/post/5c921b9ce51d45105852d8fe

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
牛顿法迭代法是一种用于解方程的数值方法,其中包括平方根。在C++中,可以使用以下步骤来实现牛顿法迭代法平方根: 1. 首先,选择一个初始猜测值作为平方根近似值。 2. 使用牛顿迭代公式进行迭代计算,直到满足预设的精度要为止。 3. 牛顿迭代公式为:x = (x + n / x) / 2,其中x是当前的近似值,n是待平方根的数。 4. 重复步骤3,直到满足预设的精度要。可以通过设置一个误差范围或者迭代次数来控制迭代的停止条件。 下面是一个使用牛顿法迭代法平方根的C++代码示例: ```cpp #include <iostream> #include <cmath> double sqrtNewton(double n, double x0, double epsilon, int maxIterations) { double x = x0; int iterations = 0; while (iterations < maxIterations) { double fx = x * x - n; // 计算函数值 double fpx = 2 * x; // 计算导数值 if (std::abs(fx) < epsilon) { // 判断是否满足精度要 break; } x = x - fx / fpx; // 更新近似值 iterations++; } return x; } int main() { double n = 16; // 待平方根的数 double x0 = 1; // 初始猜测值 double epsilon = 0.0001; // 精度要 int maxIterations = 100; // 最大迭代次数 double result = sqrtNewton(n, x0, epsilon, maxIterations); std::cout << "平方根近似值为:" << result << std::endl; return 0; } ``` 在上述代码中,我们定义了一个`sqrtNewton`函数来实现牛顿法迭代平方根。在`main`函数中,我们设置了待平方根的数`n`、初始猜测值`x0`、精度要`epsilon`和最大迭代次数`maxIterations`,然后调用`sqrtNewton`函数来计算平方根近似值,并输出结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值