0

I am trying to calculate Fibonacci numbers using the following identity: 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.

Pedja
  • 373
  • 4
  • 14
  • _Tip:_ create the algorithm for long, and then turn long into BigInteger. And `new BigInteger[][] {...}`. As matrix multiplication is associative this algorithm should work. Also odd exponent can be calculated in the even case by conditional muliplication. – Joop Eggen Nov 18 '19 at 11:02

2 Answers2

1

The problem is that multiply1 modifies the matrix you pass in, instead of making a new one. For the odd case, you need to do two multiplications with x, but the first one destroys it.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
0

Solved!

Corrected 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 void  multiply(BigInteger F[][], BigInteger M[][]) 
{ 


BigInteger x =  (F[0][0].multiply(M[0][0])).add(F[0][1].multiply(M[1][0])); 
BigInteger y =  (F[0][0].multiply(M[0][1])).add(F[0][1].multiply(M[1][1])); 
BigInteger z =  (F[1][0].multiply(M[0][0])).add(F[1][1].multiply(M[1][0]));
BigInteger w =  (F[1][0].multiply(M[0][1])).add(F[1][1].multiply(M[1][1]));



F[0][0] = x; 
F[0][1] = y; 
F[1][0] = z; 
F[1][1] = w;


} 

static void multiply2(BigInteger F[][]) 
{ 
BigInteger x =  (F[0][0].multiply(BigInteger.ONE)).add(F[0][1].multiply(BigInteger.ZERO)); 
BigInteger y =  (F[1][0].multiply(BigInteger.ONE)).add(F[1][1].multiply(BigInteger.ZERO)); 


F[0][0] = x; 
F[0][1] = y; 


} 


static void expBySquaring(BigInteger F[][], BigInteger n)
        {
  if( n.equals(BigInteger.ZERO) || n.equals(BigInteger.ONE))          
   return; 
BigInteger M[][] = new BigInteger[][]{{BigInteger.ONE,BigInteger.ONE},{BigInteger.ONE,BigInteger.ZERO}}; 
 expBySquaring(F,n.shiftRight(1));
 multiply(F,F);
 if(n.mod(BigInteger.valueOf(2)).equals(BigInteger.ONE))
 multiply(F, M);
}





/**
 * @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));
}

}
Pedja
  • 373
  • 4
  • 14