I am trying to calculate Fibonacci numbers using the following identity: , and repeated squaring algorithm given on this page.
Here is my Java code:
import java.math.BigInteger;
import java.util.Scanner;
/**
*
* @author pedja
*/
public class Fibonacci {
static BigInteger fib(BigInteger n)
{
BigInteger F[][] = new BigInteger[][]{{BigInteger.ONE,BigInteger.ONE},{BigInteger.ONE,BigInteger.ZERO}};
if (n.equals(BigInteger.ZERO)) {
return BigInteger.ZERO;
}
else {
expBySquaring(F, n.subtract(BigInteger.ONE));
multiply2(F);
return F[0][0];
}
}
static BigInteger[][] multiply1(BigInteger N[][], BigInteger M[][])
{
BigInteger x = (N[0][0].multiply(M[0][0])).add(N[0][1].multiply(M[1][0]));
BigInteger y = (N[0][0].multiply(M[0][1])).add(N[0][1].multiply(M[1][1]));
BigInteger z = (N[1][0].multiply(M[0][0])).add(N[1][1].multiply(M[1][0]));
BigInteger w = (N[1][0].multiply(M[0][1])).add(N[1][1].multiply(M[1][1]));
N[0][0] = x;
N[0][1] = y;
N[1][0] = z;
N[1][1] = w;
return N;
}
static BigInteger[][] multiply2(BigInteger A[][])
{
BigInteger x = (A[0][0].multiply(BigInteger.ONE)).add(A[0][1].multiply(BigInteger.ZERO));
BigInteger y = (A[1][0].multiply(BigInteger.ONE)).add(A[1][1].multiply(BigInteger.ZERO));
A[0][0] = x;
A[0][1] = y;
return A;
}
static BigInteger [][] expBySquaring(BigInteger x[][], BigInteger n)
{
if (n.equals(BigInteger.ZERO)) {
BigInteger[][] result=new BigInteger[][] {{BigInteger.ONE,BigInteger.ZERO},{BigInteger.ZERO,BigInteger.ONE}};
return result;
}
else if (n.equals(BigInteger.ONE)) {
return x;
}
else if (n.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) {
return expBySquaring(multiply1(x,x), n.divide(BigInteger.valueOf(2)));
}
else {
return multiply1(x,expBySquaring(multiply1(x,x),(n.subtract(BigInteger.ONE)).divide(BigInteger.valueOf(2))));
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
Scanner input = new Scanner(System.in);
BigInteger n;
System.out.print("Enter the ordinal number: ");
n = input.nextBigInteger();
System.out.println(fib(n));
}
}
Code produces correct result for n of the form 2^k+1. But when I want to calculate F(n) where n is not of the form 2^k+1 it's not working, so I guess that problem is in the following part of code:
else {
return multiply1(x,expBySquaring(multiply1(x,x), (n.subtract(BigInteger.ONE)).divide(BigInteger.valueOf(2))));
}
How can I fix this problem?
EDIT
Following advice from @MattTimmermans I have changed the code:
import java.math.BigInteger;
import java.util.Scanner;
/**
*
* @author pedja
*/
public class Fibonacci {
BigInteger[][] L= new BigInteger[][]{{BigInteger.ONE,BigInteger.ONE},{BigInteger.ONE,BigInteger.ONE}};
BigInteger[][] B = new BigInteger[][]{{BigInteger.ONE,BigInteger.ONE},{BigInteger.ONE,BigInteger.ONE}};
public BigInteger fib(BigInteger n)
{
BigInteger F[][] = new BigInteger[][]{{BigInteger.ONE,BigInteger.ONE},{BigInteger.ONE,BigInteger.ZERO}};
if (n.equals(BigInteger.ZERO)) {
return BigInteger.ZERO;
}
else {
expBySquaring(F, n.subtract(BigInteger.ONE));
multiply2(L);
return L[0][0];
}
}
public BigInteger[][] multiply1(BigInteger N[][], BigInteger M[][])
{
BigInteger x = (N[0][0].multiply(M[0][0])).add(N[0][1].multiply(M[1][0]));
BigInteger y = (N[0][0].multiply(M[0][1])).add(N[0][1].multiply(M[1][1]));
BigInteger z = (N[1][0].multiply(M[0][0])).add(N[1][1].multiply(M[1][0]));
BigInteger w = (N[1][0].multiply(M[0][1])).add(N[1][1].multiply(M[1][1]));
L[0][0] = x;
L[0][1] = y;
L[1][0] = z;
L[1][1] = w;
return L;
}
public BigInteger[][] multiply2(BigInteger A[][])
{
BigInteger x = (A[0][0].multiply(BigInteger.ONE)).add(A[0][1].multiply(BigInteger.ZERO));
BigInteger y = (A[1][0].multiply(BigInteger.ONE)).add(A[1][1].multiply(BigInteger.ZERO));
B[0][0] = x;
B[0][1] = y;
return B;
}
public BigInteger [][] expBySquaring(BigInteger x[][], BigInteger n)
{
if (n.equals(BigInteger.ZERO)) {
BigInteger[][] result=new BigInteger[][] {{BigInteger.ONE,BigInteger.ZERO},{BigInteger.ZERO,BigInteger.ONE}};
return result;
}
else if (n.equals(BigInteger.ONE)) {
return x;
}
else if (n.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) {
return expBySquaring(multiply1(x,x), n.divide(BigInteger.valueOf(2)));
}
else {
return multiply1(x,expBySquaring(multiply1(x,x),(n.subtract(BigInteger.ONE)).divide(BigInteger.valueOf(2))));
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
Scanner input = new Scanner(System.in);
BigInteger n;
System.out.print("Enter the ordinal number: ");
n = input.nextBigInteger();
Fibonacci fibonacci = new Fibonacci();
System.out.println(fibonacci.fib(n));
}
}
Now it produces correct result only for n of the form 2^k+1 and 2^k+2.