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

Posted

技术标签:

【中文标题】牛顿方法有更优雅的 Go 实现吗?【英文标题】:Is there a more elegant Go implementation of Newton's method? 【发布时间】:2015-06-22 08:41:52 【问题描述】:

我正在学习 Go 教程,我想知道是否有比 Exercise: Loops and Functions 上使用牛顿法计算平方根更优雅的方法:

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

(规范的一部分是提供迭代次数。)这里是full program,包括包声明、导入和main。

【问题讨论】:

【参考方案1】:

首先,您的算法不正确。公式为:

您对此进行了建模:

z - (z*z - x) / 2*z

但应该是:

z - (z*z - x)/2/z

或者

z - (z*z - x)/(2*z)

(你的错误公式必须运行 50 万次迭代才能接近0.001!正确的公式使用 4 次迭代才能接近1e-6,以防x = 2。)

接下来,z=1 的初始值对于随机数来说并不是最好的(它可能适用于像 2 这样的小数)。您可以使用z = x / 2 开始,这是一个非常简单的初始值,可以让您以更少的步骤更接近结果。

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

您可以将结果命名为z,这样返回语句就可以“裸露”。如果您将当前的“退出”条件移动到循环中,您还可以创建一个循环变量来计算迭代次数,如果满足,您可以打印迭代次数并且可以简单地返回。也可以将计算移到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
        
    

您还可以将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
        
    

注意:我以1 开始我的迭代计数器,因为在我的情况下,“退出”条件在for 内,而不是for 的条件。

【讨论】:

是的!感谢您回答我应该提出的问题(我的算法是否正确?)以及我提出的问题。【参考方案2】:
package main

import (
    "fmt"
    "math"
)

func Sqrt(x float64) float64 
    z := 1.0
    // First guess
    z -= (z*z - x) / (2*z)
    // Iterate until change is very small
    for zNew, delta := z, z; delta > 0.00000001; z = zNew 
        zNew -= (zNew * zNew - x) / (2 * zNew)
        delta = z - zNew
    
    return z


func main() 
    fmt.Println(Sqrt(2))
    fmt.Println(math.Sqrt(2))

【讨论】:

以上是关于牛顿方法有更优雅的 Go 实现吗?的主要内容,如果未能解决你的问题,请参考以下文章

无约束优化问题中牛顿法与拟牛顿法四种迭代方法的matlab实现

无约束优化问题中牛顿法与拟牛顿法四种迭代方法的matlab实现

采用Armjio准则求步长的BFGS/DFP拟牛顿方法--MATLAB实现

采用Armjio准则求步长的BFGS/DFP拟牛顿方法--MATLAB实现

牛顿法

二分查找法和牛顿法求根号的实现