牛顿的方法有更优雅的Go实现吗?

Ell*_*tus 4 go newtons-method

我正在做Go教程,我想知道是否有更优雅的方法来计算平方根,使用Newton的方法练习:循环和函数比这个:

func Sqrt(x float64) float64 {
    count := 0
    var old_z, z float64 = 0, 1
    for ; math.Abs(z-old_z) > .001; count++ {
        old_z, z = z, z - (z*z - x) / 2*z
    }
    fmt.Printf("Ran %v iterations\n", count)
    return z
}
Run Code Online (Sandbox Code Playgroud)

(规范的一部分是提供迭代次数.)这是完整的程序,包括package语句,导入和main.

icz*_*cza 8

首先,你的算法不正确.公式是:

在此输入图像描述

你用以下方法建模:

z - (z*z - x) / 2*z
Run Code Online (Sandbox Code Playgroud)

但它应该是:

z - (z*z - x)/2/z
Run Code Online (Sandbox Code Playgroud)

要么

z - (z*z - x)/(2*z)
Run Code Online (Sandbox Code Playgroud)

(你的不正确的公式必须运行五十万次迭代,甚至只能得到尽可能接近0.001!正确的公式使用4次迭代来获得尽可能接近1e-6的情况x = 2.)

接下来,初始值z=1不是随机数的最佳值(它可能适用于少数类似的2).你可以开始使用z = x / 2一个非常简单的初始值,并以更少的步骤更接近结果.

其他选项不一定使其更具可读性或优雅,它是主观的:

您可以将结果命名为,z以便return语句可以"裸".如果将当前的"退出"条件移动到循环中,您也可以创建一个循环变量来计算迭代次数,如果满足,则打印迭代计数并且可以简单地返回.您还可以将计算移动到以下的初始化部分if:

func Sqrt(x float64) (z float64) {
    z = x / 2
    for i, old := 1, 0.0; ; i++ {
        if old, z = z, z-(z*z-x)/2/z; math.Abs(old-z) < 1e-5 {
            fmt.Printf("Ran %v iterations\n", i)
            return
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你也可以移动z = x / 2到初始化部分for但是你不能有命名结果(否则z会创建一个会影响命名返回值的局部变量):

func Sqrt(x float64) float64 {
    for i, z, old := 1, x/2, 0.0; ; i++ {
        if old, z = z, z-(z*z-x)/2/z; math.Abs(old-z) < 1e-5 {
            fmt.Printf("Ran %v iterations\n", i)
            return z
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:我启动了迭代计数器,1因为我的情况下的"退出"条件是在内部for而不是条件for.

  • 耶!感谢您回答我应该问的问题(我的算法是否正确?)以及我问的问题。 (2认同)