2015-04-16 50 views
4

我做的围棋教程,我想知道是否有一个更优雅的方式使用牛顿法计算的Exercise: Loops and Functions比这平方根:牛顿方法有更优雅的Go实现吗?

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语句,imports和main。

回答

9

首先,你算法不正确。其计算公式为:

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-6x = 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 
     } 
    } 
} 

您也可以移动z = x/2for的初始化部分,但是,你不能有一个名为结果(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的条件。

+1

Yipes!感谢您回答我应该问的问题(我的算法是否正确?)以及我问的问题。 –