181 lines
5.3 KiB
Java
181 lines
5.3 KiB
Java
import java.io.*;
|
|
import java.util.*;
|
|
import java.math.*;
|
|
|
|
/**
|
|
* Problem : Find the n-th fibonacci number. Let's call it F(n).
|
|
* As we know F(n) grows exponentially. So, we need to find
|
|
* the F(n) % mod, where mod is [10^9 + 7].
|
|
*
|
|
* Constraints : 0 <= n <= 10^19
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
*
|
|
* Solution : nth-Fibonacci is nothing but a sum of previous two fibonacci numbers
|
|
*
|
|
* i.e. F(0) = 0, if n == 0;
|
|
* F(1) = 1, if n == 1;
|
|
* F(n) = F(n-1) + F(n-2) , For all n>1;
|
|
*
|
|
* Let's take n = 5 for dry run purpose and analyse the time complexity.
|
|
*
|
|
* Approach 1: Just iteratively calculate using loop.
|
|
*
|
|
* fprev_2 = 0;
|
|
* fprev_1 = 1;
|
|
*
|
|
* if n is equal to 0, then return fprev_2
|
|
* if n is equal to 1, then return fprev_1
|
|
*
|
|
*
|
|
* if both above conditions fail, then
|
|
*
|
|
* while n greater than 1:
|
|
* fcur = fprev_2 + f_prev1
|
|
* fprev_2 = fprev_1
|
|
* fprev_1 = fcur
|
|
* n = n-1
|
|
*
|
|
* return fprev1;
|
|
*
|
|
* As expected time complexity of this approach is O(n),
|
|
* which simply gives TLE(Time Limit Exceeded) for the above constraints.
|
|
* So we need something faster.
|
|
* ......................................................................
|
|
*
|
|
* Approach 2: Use Matrix Exponentiation, this idea comes from
|
|
* binary exponentiation and associativity property
|
|
* of matrix multiplication.
|
|
*
|
|
* Let's understand ASSOCIATIVITY:
|
|
*
|
|
* A * (B * C) = (A * B) * C
|
|
*
|
|
* where A, B, C are operands of some data type,
|
|
* it simply indicates final result is independent of order of operations.
|
|
*
|
|
* BINARY EXPONENTIATION:
|
|
*
|
|
*
|
|
* pow(base, exponenent):
|
|
* result = 1
|
|
* while exponent greater than 0:
|
|
* if exponent is odd: result = result * base
|
|
* base = base * base
|
|
* exponent = exponent / 2
|
|
*
|
|
* return result
|
|
*
|
|
* what it does is that it calculates the power using log(exponent) operations only
|
|
* because each time exponent becomes half. Time complexity - O(log(exponent))
|
|
* which is must faster than O(exponent).
|
|
*
|
|
* Now apply this idea,
|
|
*
|
|
* we know that F(n) = F(n-1) + F(n-2)
|
|
*
|
|
* F(n-1) = 0 * F(n-2) + 1 * F(n-1)
|
|
* F(n) = 1 * F(n-1) + 1 * F(n-2)
|
|
*
|
|
*
|
|
* [[F(n-1)], [F(n)]] = [[0 1],[1 1]] * [[F(n-1)], [F(n-2)]]
|
|
* [[F(n-1)], [F(n)]] = { [[0 1],[1 1]] } ^ 2 * [[F(n-2)], [F(n-3)]]
|
|
* .
|
|
* .
|
|
* .
|
|
* .
|
|
* [[F(n-1)], [F(n)]] = { [[0 1],[1 1]] } ^ (n-1) * [[F(1)], [F(0)]]
|
|
*
|
|
* Thus we can calculate the F(n) using time complexity O(8 * log(n)) the constant
|
|
* 8 comes because of matrix multiplication of two 2x2 matrices.
|
|
*
|
|
* Below is the implementation in JAVA.
|
|
*
|
|
* First few terms of fibonacci sequence
|
|
* 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89...
|
|
*
|
|
* */
|
|
|
|
|
|
public class Nth_Fibonacci {
|
|
|
|
static class Matrix {
|
|
/*data member*/
|
|
public long[][] matrix;
|
|
|
|
|
|
/*Constructor*/
|
|
public Matrix(int n) {
|
|
matrix=new long[n][n];
|
|
}
|
|
|
|
/**
|
|
* @param other Matrix
|
|
* @param mod
|
|
*
|
|
* this method performs matrix multiplication of this.matrix
|
|
* and other.matrix and returns the product matrix
|
|
* */
|
|
public Matrix multiply(Matrix other,int mod) {
|
|
|
|
Matrix product = new Matrix(matrix.length);
|
|
|
|
for (int i = 0; i < matrix.length; i++) {
|
|
for (int j = 0; j < matrix.length; j++) {
|
|
for (int k = 0; k < matrix.length; k++) {
|
|
product.matrix[i][k] = (product.matrix[i][k] + matrix[i][j] * other.matrix[j][k])%mod;
|
|
}
|
|
}
|
|
}
|
|
|
|
return product;
|
|
}
|
|
|
|
}
|
|
|
|
/*binary exponentiation using matrices*/
|
|
public static Matrix exponentiation(Matrix m,BigInteger n,int mod) {
|
|
|
|
Matrix expo = new Matrix(m.matrix.length);
|
|
|
|
for (int i = 0; i < m.matrix.length; i++) expo.matrix[i][i]=1;
|
|
|
|
while(n.compareTo(BigInteger.valueOf(0))>0) {
|
|
|
|
if(n.remainder(BigInteger.valueOf(2)).compareTo(BigInteger.valueOf(1)) == 0) expo = expo.multiply(m,mod);
|
|
|
|
m = m.multiply(m,mod);
|
|
|
|
n = n.divide(BigInteger.valueOf(2));
|
|
}
|
|
|
|
return expo;
|
|
}
|
|
|
|
public static void main(String []args) throws IOException {
|
|
|
|
/*for taking input.*/
|
|
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
|
|
|
/*because of constraints ,I read the number in string since it does not fit in long as well.*/
|
|
String s = br.readLine().trim();
|
|
|
|
/*convert the string to bigInteger to be able to perform some arithmetic operations*/
|
|
BigInteger n = new BigInteger(s);
|
|
|
|
|
|
int mod = (int)1e9+7;
|
|
|
|
Matrix A = new Matrix(2);
|
|
A.matrix[0][0] = 0;
|
|
A.matrix[0][1] = A.matrix[1][0] = A.matrix[1][1] = 1;
|
|
|
|
Matrix ans = exponentiation(A,n,mod);
|
|
|
|
int result = (int)(ans.matrix[0][1] % mod) ;
|
|
|
|
System.out.println(n + "-th Fibonacci number is : " + result);
|
|
}
|
|
|
|
}
|