From 676d1257e53cc58e3b1391ad0fc3f6e9084a43b9 Mon Sep 17 00:00:00 2001 From: B Jagdish Reddy <67193081+rgoewedky@users.noreply.github.com> Date: Mon, 17 Jan 2022 23:59:19 +0530 Subject: [PATCH] chore(Java): added fibonacci using matrix exponentiation (#540) Co-authored-by: Ming Tsai <37890026+ming-tsai@users.noreply.github.com> --- algorithms/Java/Maths/Nth_Fibonacci.java | 180 +++++++++++++++++++++++ algorithms/Java/README.md | 1 + 2 files changed, 181 insertions(+) create mode 100644 algorithms/Java/Maths/Nth_Fibonacci.java diff --git a/algorithms/Java/Maths/Nth_Fibonacci.java b/algorithms/Java/Maths/Nth_Fibonacci.java new file mode 100644 index 00000000..b848f593 --- /dev/null +++ b/algorithms/Java/Maths/Nth_Fibonacci.java @@ -0,0 +1,180 @@ +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); + } + +} diff --git a/algorithms/Java/README.md b/algorithms/Java/README.md index 7c3329aa..70c32fad 100644 --- a/algorithms/Java/README.md +++ b/algorithms/Java/README.md @@ -29,6 +29,7 @@ ## Maths - [Factorial](Maths/factorial_using_big_integer.java) +- [Nth-Fibonacci](Maths/Nth_Fibonacci.java) - [Catalan Numbers](Maths/catalan-numbers.java) - [Nth Geek Onacci Number](Maths/nth-geek-onacci-number.java) - [Random Node in Linked List](Maths/algorithms_random_node.java)