Reputation: 35
I am trying to write a function that returns the nth number of the Fibonacci Sequence. I am running into trouble when going from returning an int to a big.Int.
Here is the normal version, which just uses matrix exponentiation to find the nth number of the Fibonacci sequence. It works just fine and returns the value I want:
func normalFib(n int) int {
if n == 0 || n == 1 {
return n
}
n -= 2
a, b, c := 1, 1, 0
x, y := 1, 1
var evenNum [3]int
var oddNum [2]int
for n > 0 {
if n%2 == 0 {
temp := []int{a*a + b*b, a*b + b*c, b*b + c*c}
a, b, c = temp[0], temp[1], temp[2]
copy(evenNum[:], temp)
n /= 2
} else {
temp := []int{x*a + y*b, x*b + y*c}
x, y = temp[0], temp[1]
copy(oddNum[:], temp)
n--
}
}
return oddNum[0]
}
func main() {
fmt.Println(normalFib(10)) // Outputs 55
}
The intermediate values of the above function look like this:
The value of N is currently: 8
The values of a, b, c: 2 1 1
The value of N is currently: 4
The values of a, b, c: 5 3 2
The value of N is currently: 2
The values of a, b, c: 34 21 13
The value of N is currently: 1
The values of x, y: 55 34 // x is correct!
This is the big.Int version. Because of the way the big.Add() and big.Mul() functions work, I've tried working with it by
Despite what I've tried, it will still only output multiples of two, which is not what I want:
func bigFib(n int) *big.Int {
if n == 0 || n == 1 {
return big.NewInt(int64(n))
}
n -= 2
a, b, c := big.NewInt(1), big.NewInt(1), big.NewInt(0)
x, y := big.NewInt(1), big.NewInt(0)
var evenNum [3]*big.Int
var oddNum [2]*big.Int
for n > 0 {
if n%2 == 0 {
// Result variables
d, e, f := big.NewInt(0), big.NewInt(0), big.NewInt(0)
// Temporary slice to hold the result of the matrix addition.
temp := []*big.Int{
d.Add(e.Mul(a, a), f.Mul(b, b)),
d.Add(e.Mul(a, b), f.Mul(b, c)),
d.Add(e.Mul(b, b), f.Mul(c, c))}
// Setting a, b, c to the values of the slice.
a, b, c = temp[0], temp[1], temp[2]
// Copying the temp slice to the evenNum array.
copy(evenNum[:], temp)
n /= 2
} else {
// Result variables
d, e, f := big.NewInt(0), big.NewInt(0), big.NewInt(0)
// Temprorary slice to hold the result of the matrix addition.
temp := []*big.Int{
d.Add(e.Mul(x, a), f.Mul(y, b)),
d.Add(e.Mul(x, b), f.Mul(y, c))}
// Set x, y to the values of the slice.
x, y = temp[0], temp[1]
// Copying the temp slice to the oddNum array.
copy(oddNum[:], temp)
n--
}
}
return oddNum[0]
}
func main() {
fmt.Println(bigFib(10)) // Outputs 8
}
The intermediate values for the bigFib function look like this:
The value of N is currently: 8
Value of d, e, f: 1 1 0
Value of a, b, c: 1 1 1
The value of N is currently: 4
Value of d, e, f: 2 1 1
Value of a, b, c: 2 2 2
The value of N is currently: 2
Value of d, e, f: 8 4 4
Value of a, b, c: 8 8 8
The value of N is currently: 1
Value of d, e, f: 8 8 0
Value of x, y: 8 8
If anyone can show me what is going wrong, it would be greatly appreciated.
Upvotes: 3
Views: 6370
Reputation: 25918
As the type name suggests, a *big.Int
is a pointer, and per the documentation for big.Int.Add
:
Add sets z to the sum x+y and returns z.
The "returns z
" is important, because it means that when you do this:
temp := []*big.Int{
d.Add(e.Mul(a, a), f.Mul(b, b)),
d.Add(e.Mul(a, b), f.Mul(b, c)),
d.Add(e.Mul(b, b), f.Mul(c, c))
}
all three elements of your slice end up being pointers to the same big.Int
, i.e. to d
. Each of your three Add
calls may change d
, but they're all changing (and returning a pointer to) the same, single object, which is not what you want.
To avoid this behavior, you need to create a new, distinct object for each distinct result, something like:
temp := []*big.Int{
big.NewInt(0).Add(big.NewInt(0).Mul(a, a), big.NewInt(0).Mul(b, b)),
big.NewInt(0).Add(big.NewInt(0).Mul(a, b), big.NewInt(0).Mul(b, c)),
big.NewInt(0).Add(big.NewInt(0).Mul(b, b), big.NewInt(0).Mul(c, c)),
}
If you want to minimize allocations, you should be able to do:
x, y := big.NewInt(0), big.NewInt(0)
temp := []*big.Int{
big.NewInt(0).Add(x.Mul(a, a), y.Mul(b, b)),
big.NewInt(0).Add(x.Mul(a, b), y.Mul(b, c)),
big.NewInt(0).Add(x.Mul(b, b), y.Mul(c, c)),
}
since the Adds
are done in order and the pointers to x
and y
are not retained, so the fact you're reusing copies of them doesn't cause a problem. But for the three elements of your slice, you need distinct objects.
Upvotes: 6