Golang实现大数的n次方根计算
Golang实现大数的n次方根计算 我在Go的大数包文档中找不到计算n次方根的方法,所以决定自己使用牛顿法来实现。我在这个链接找到了一个解决方案,并开始用Go实现。但我的代码只在底数为2时工作正常。对于其他底数,结果与正确值相差甚远。
有人能指出我哪里错了吗?
以下是我的代码:
package main
import (
"fmt"
"math/big"
"math/rand"
)
// PowFloat return x^n
func PowFloat(x *big.Float, n int64) *big.Float {
result := new(big.Float).SetInt64(1)
for i := 0; i < int(n); i++ {
result.Mul(result, x)
}
return result
}
// GetNthRoothFloat get nth root of a using newton's method
func GetNthRoothFloat(a *big.Float, n int64) *big.Float {
N := new(big.Float).SetInt64(n)
xPre := new(big.Float).SetInt64(int64(rand.Intn(10) + 1))
eps := new(big.Float).SetFloat64(0.00000000001)
delX := new(big.Float).SetInt64(2147483647)
xK := new(big.Float).SetInt64(0)
for delX.Cmp(eps) > 0 {
t1 := new(big.Float).Sub(N, new(big.Float).SetFloat64(1.0)) // t1 = n-1
t1 = t1.Mul(t1, xPre) // t1 = (N-1) * xPre
t2 := new(big.Float).Quo(a, PowFloat(xPre, n-1)) // t2 = a/( xPre^(n-1) )
t3 := new(big.Float).Add(t1, t2) // t3 = t1 + t2
xK.Quo(t3, N)
delX = new(big.Float).Sub(xK, xPre)
delX.Abs(delX)
xPre = xK.Copy(xK)
}
return xK
}
func main() {
t := new(big.Float).SetInt64(64)
fmt.Println(GetNthRoothFloat(t, 3))
}
更多关于Golang实现大数的n次方根计算的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
公式为
x(K + 1) = (1 / N) * ((N - 1) * x(K) + A / x(K) ^ (N - 1))
我认为你实现得没问题。
不过,我对你的初始近似值持怀疑态度。尝试使用更接近正确值的数值。
你可以使用 math.Pow 函数以 float64 类型来计算初始近似值。
每次迭代时打印该值应该能提供一些线索。
更多关于Golang实现大数的n次方根计算的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你的牛顿法实现存在两个关键问题:1)初始猜测值范围太小 2)迭代公式有误。以下是修正后的实现:
package main
import (
"fmt"
"math/big"
"math/rand"
)
// PowFloat 计算 x^n
func PowFloat(x *big.Float, n int64) *big.Float {
result := new(big.Float).SetFloat64(1.0)
for i := int64(0); i < n; i++ {
result.Mul(result, x)
}
return result
}
// GetNthRootFloat 使用牛顿法计算n次方根
func GetNthRootFloat(a *big.Float, n int64) *big.Float {
if a.Cmp(new(big.Float).SetFloat64(0.0)) == 0 {
return new(big.Float).SetFloat64(0.0)
}
// 更好的初始猜测:使用a的1/n次方作为近似
guess := new(big.Float).SetFloat64(1.0)
if a.Cmp(guess) > 0 {
// 当a>1时,初始值设为a/2
guess.Quo(a, new(big.Float).SetFloat64(2.0))
} else {
// 当0<a<1时,初始值设为a*2
guess.Mul(a, new(big.Float).SetFloat64(2.0))
}
eps := new(big.Float).SetFloat64(1e-15)
nFloat := new(big.Float).SetInt64(n)
nMinus1 := new(big.Float).SetInt64(n - 1)
for {
// 牛顿迭代公式: x_{k+1} = (1/n)*((n-1)*x_k + a/(x_k^(n-1)))
xn1 := PowFloat(guess, n-1)
term1 := new(big.Float).Mul(nMinus1, guess)
term2 := new(big.Float).Quo(a, xn1)
nextGuess := new(big.Float).Add(term1, term2)
nextGuess.Quo(nextGuess, nFloat)
// 检查收敛
diff := new(big.Float).Sub(nextGuess, guess)
diff.Abs(diff)
if diff.Cmp(eps) < 0 {
return nextGuess
}
guess = nextGuess
}
}
func main() {
// 测试用例
tests := []struct {
value float64
n int64
}{
{64, 3}, // 立方根
{27, 3}, // 3
{256, 4}, // 4
{1024, 10}, // 约2
{2, 2}, // 平方根
}
for _, test := range tests {
a := new(big.Float).SetFloat64(test.value)
result := GetNthRootFloat(a, test.n)
fmt.Printf("%v的%v次方根 = %v\n", test.value, test.n, result)
// 验证结果
powered := PowFloat(result, test.n)
fmt.Printf("验证: %v^%v = %v\n\n", result, test.n, powered)
}
}
主要修正点:
-
初始猜测优化:原代码使用随机数1-10作为初始值,对于大数或小数效果不佳。修正后根据a的大小动态调整初始猜测。
-
正确的牛顿迭代公式:原代码的迭代公式有误。正确的牛顿法求n次方根公式为:
x_{k+1} = (1/n) * ((n-1)*x_k + a/(x_k^(n-1))) -
收敛条件改进:使用更精确的收敛判断,避免无限循环。
-
边界处理:添加了对a=0的特殊情况处理。
这个实现可以正确计算任意正数的n次方根。对于负数,需要额外处理奇偶次方根的情况。

