是否有更优雅的Go实现Newton的方法?

时间:2015-04-16 05:48:13

标签: go newtons-method

我正在做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,包括package语句,导入和main。

2 个答案:

答案 0 :(得分:8)

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

enter image description here

您使用以下方式对此进行了建模:

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 1}}。)

接下来,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开始了我的迭代计数器,因为&#34;退出&#34;在我的情况下,条件位于for内,而不是for的条件。

答案 1 :(得分:0)

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))
}