是否有Go函数来获取大整数的立方根?

时间:2015-07-06 05:24:01

标签: algorithm go

我有一个big.Int变量,希望找到它的立方根。

这是在图书馆的某个地方实现的吗? Exp函数似乎只取整数,big.Rat似乎完全没有Exp。

2 个答案:

答案 0 :(得分:2)

可悲的是,math/big包中没有这样的功能。这意味着你必须自己推出一些东西。最容易理解和实施的是Newton's method

您只需选择一些起始编号x_0并使用递归公式enter image description here

您必须按以下方式使用它:让您的整数为b。然后是您的x^3 = b^3f(x) = x^3 - b^3以及f'(x) = 3 * x^2

所以你需要迭代: x_{n+1}=x_n - \frac{x_{n}^{3} + b^3}{3x_{n}^{2}}

check this link带有公式的图像,SO很容易插入数学公式)。

您从猜测开始,并在前一个x_n接近下一个x_n时结束。你有多接近决定。

P.S.1 你可以寻找更复杂的数值方法来找到根(你需要更少的迭代来收敛)

P.S.2 如果您需要,我在Go中写了method for calculating arbitrary precision square roots

答案 1 :(得分:0)

我已经为big.Int实现了立方根函数,都使用了简单的二进制搜索和牛顿的方法(如Salvador Dali的公式)。尽管我很确定还有改进的余地,但这是我得到的代码:

var (
    n0  = big.NewInt(0)
    n1  = big.NewInt(1)
    n2  = big.NewInt(2)
    n3  = big.NewInt(3)
    n10 = big.NewInt(10)
)

func cbrtBinary(i *big.Int) (cbrt *big.Int, rem *big.Int) {
    var (
        guess = new(big.Int).Div(i, n2)
        dx    = new(big.Int)
        absDx = new(big.Int)
        minDx = new(big.Int).Abs(i)
        step  = new(big.Int).Abs(new(big.Int).Div(guess, n2))
        cube  = new(big.Int)
    )
    for {
        cube.Exp(guess, n3, nil)
        dx.Sub(i, cube)
        cmp := dx.Cmp(n0)
        if cmp == 0 {
            return guess, n0
        }

        absDx.Abs(dx)
        switch absDx.Cmp(minDx) {
        case -1:
            minDx.Set(absDx)
        case 0:
            return guess, dx
        }

        switch cmp {
        case -1:
            guess.Sub(guess, step)
        case +1:
            guess.Add(guess, step)
        }

        step.Div(step, n2)
        if step.Cmp(n0) == 0 {
            step.Set(n1)
        }
    }
}

func cbrtNewton(i *big.Int) (cbrt *big.Int, rem *big.Int) {
    var (
        guess   = new(big.Int).Div(i, n2)
        guessSq = new(big.Int)
        dx      = new(big.Int)
        absDx   = new(big.Int)
        minDx   = new(big.Int).Abs(i)
        cube    = new(big.Int)
        fx      = new(big.Int)
        fxp     = new(big.Int)
        step    = new(big.Int)
    )
    for {
        cube.Exp(guess, n3, nil)
        dx.Sub(i, cube)
        cmp := dx.Cmp(n0)
        if cmp == 0 {
            return guess, n0
        }

        fx.Sub(cube, i)
        guessSq.Exp(guess, n2, nil)
        fxp.Mul(n3, guessSq)
        step.Div(fx, fxp)
        if step.Cmp(n0) == 0 {
            step.Set(n1)
        }

        absDx.Abs(dx)
        switch absDx.Cmp(minDx) {
        case -1:
            minDx.Set(absDx)
        case 0:
            return guess, dx
        }

        guess.Sub(guess, step)
    }
}

令人惊讶的是,简单的二进制算法也是最快的:

BenchmarkCbrt/binary/10e6-4       100000         19195 ns/op
BenchmarkCbrt/binary/10e12-4       30000         43634 ns/op
BenchmarkCbrt/binary/10e18-4       20000         73334 ns/op
BenchmarkCbrt/newton/10e6-4        30000         47027 ns/op
BenchmarkCbrt/newton/10e12-4       10000        123612 ns/op
BenchmarkCbrt/newton/10e18-4       10000        214884 ns/op

这是完整的代码,包括测试和基准测试:https://play.golang.org/p/uoEmxRK5jgs