2015-04-16 125 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!感謝您回答我應該問的問題(我的算法是否正確?)以及我問的問題。 –