今年の3月まで19ヶ月ほどGoを書いていたのですが、その際は何となく雰囲気でやっていたので。
Goを基礎から勉強しなおそうと思い、A Tour of Goをやっています。
平方根の計算実装メモ
問題
平方根を求めるmath.Sqrtの実装を通して、ループの処理を学ぶような内容なのですが、
あいにく数学は高校生の頃以来でして、平方根や絶対値・計算機イプシロンなどの理解に苦しんだので記録を残します。
平方根の基本実装〜
Implement this in the func Sqrt provided.
A decent starting guess for z is 1, no matter what the input.
To begin with, repeat the calculation 10 times and print each z along the way.
See how close you get to the answer for various values of x (1, 2, 3, ...) and how quickly the guess improves.
(これをfunc Sqrtに実装してください。何が入力されてもzの適切な開始推測値は1です。まず計算を10回繰り返してそれぞれのzを表示します。
x (1, 2, 3, ...)のさまざまな値に対する答えがどれほど近似し、推測が速くなるかを確認してください。)
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
// A decent starting guess for z is 1, no matter what the input.
z := 1.0
// To begin with, repeat the calculation 10 times and print each z along the way.
for i := 0; i < 10; i++ {
z -= (z*z - x) / (2 * z)
}
return z
}
func main() {
// See how close you get to the answer for various values of x (1, 2, 3, ...) and how quickly the guess improves.
for i := 0; i < 10; i++ {
x := float64(i + 1)
fmt.Println(Sqrt(x))
}
}
出力結果
1
1.414213562373095
1.7320508075688774
2
2.23606797749979
2.449489742783178
2.6457513110645907
2.8284271247461903
3
3.1622776601683795
平方根の最終計算調整
Next, change the loop condition to stop once the value has stopped changing (or only changes by a very small amount).
See if that's more or fewer than 10 iterations.
Try other initial guesses for z, like x, or x/2. How close are your function's results to the math.Sqrt in the standard library?
(次に値が変化しなくなった(もしくはごくわずかな変化しかしなくなった)場合にループを停止させます。それが10回よりも多いか少ないかを確認してください。
xやx/2 のように他の初期推測の値をzに与えてみてください。あなたの関数の結果は標準ライブラリのmath.Sqrtにどれくらい近づきましたか?)
- 絶対値→基準となる0からどれだけ離れているかを示す値。
- 計算機イプシロン→浮動小数点数において、「1より大きい最小の数」と1との差。
package main
import (
"fmt"
"math"
)
var eps float64 = math.Nextafter(1, 2) - 1 // 絶対値(計算機イプシロン)
func Sqrt(x float64) float64 {
// Try other initial guesses for z, like x, or x/2.
z := 1.0
//z := x
//z := x / 2
previousZ := z
count := 0
for i := 0; i < 10; i++ {
count++
z -= (z*z - x) / (2 * z)
if math.Abs(z-previousZ) <= eps {
// Next, change the loop condition to stop once the value has stopped changing (or only changes by a very small amount).
break
}
previousZ = z
}
// See if that's more or fewer than 10 iterations.
fmt.Println(count, "times")
return z
}
func main() {
for i := 0; i < 10; i++ {
x := float64(i + 1)
fmt.Println(Sqrt(x))
}
}
z := 1.0
1 times
1
6 times
1.414213562373095
6 times
1.7320508075688774
7 times
2
7 times
2.23606797749979
7 times
2.449489742783178
7 times
2.6457513110645907
10 times
2.8284271247461903
7 times
3
10 times
3.1622776601683795
z := x
1 times
1
6 times
1.414213562373095
6 times
1.7320508075688774
7 times
2
7 times
2.23606797749979
7 times
2.449489742783178
7 times
2.6457513110645907
10 times
2.8284271247461903
7 times
3
10 times
3.1622776601683795
z := x/2
計算量が他の2つより少ない。
7 times
1
6 times
1.414213562373095
5 times
1.7320508075688774
1 times
2
5 times
2.23606797749979
6 times
2.449489742783178
6 times
2.6457513110645907
10 times
2.82842712474619
6 times
3
10 times
3.162277660168379
math.Sqrtと実装したSqrtの比較
package main
import (
"fmt"
"math"
)
func main() {
for i := 0; i < 10; i++ {
x := float64(i + 1)
fmt.Println(math.Sqrt(x))
}
}
1
1.4142135623730951
1.7320508075688772
2
2.23606797749979
2.449489742783178
2.6457513110645907
2.8284271247461903
3
3.1622776601683795
感想
仕事で業務アプリ作ってる事が多いけど、基本的な四則演算以外だと、
地図系アプリの位置情報の計算くらいしかやった記憶がない気もするし、
数学ってあまり使わないな🤔
参考
- https://www.exmedia.jp/blog/a-tour-of-go%E3%81%AE%E7%B7%B4%E7%BF%92%E5%95%8F%E9%A1%8C%E3%82%92%E8%A7%A3%E8%AA%AC%E3%81%99%E3%82%8B%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA1-11-exercise-loops-and-functions/
- https://ja.wikipedia.org/wiki/%E8%A8%88%E7%AE%97%E6%A9%9F%E3%82%A4%E3%83%97%E3%82%B7%E3%83%AD%E3%83%B3