Skip to content

Commit 7a050ab

Browse files
authored
Merge pull request #531 from rahul1995/fib-matrix-expo
Add O(log n) algorithm using matrix exponentiation to find n-th fibonacci
2 parents 15883b2 + feaca3b commit 7a050ab

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

Maths/Fibonacci.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,83 @@ const FibonacciDpWithoutRecursion = (number) => {
6969
return table
7070
}
7171

72+
// Using Matrix exponentiation to find n-th fibonacci in O(log n) time
73+
74+
const copyMatrix = (A) => {
75+
return A.map(row => row.map(cell => cell))
76+
}
77+
78+
const Identity = (size) => {
79+
const I = Array(size).fill(null).map(() => Array(size).fill())
80+
return I.map((row, rowIdx) => row.map((_col, colIdx) => {
81+
return rowIdx === colIdx ? 1 : 0
82+
}))
83+
}
84+
85+
// A of size (l x m) and B of size (m x n)
86+
// product C will be of size (l x n)
87+
const matrixMultiply = (A, B) => {
88+
A = copyMatrix(A)
89+
B = copyMatrix(B)
90+
const l = A.length
91+
const m = B.length
92+
const n = B[0].length // Assuming non-empty matrices
93+
const C = Array(l).fill(null).map(() => Array(n).fill())
94+
for (let i = 0; i < l; i++) {
95+
for (let j = 0; j < n; j++) {
96+
C[i][j] = 0
97+
for (let k = 0; k < m; k++) {
98+
C[i][j] += A[i][k] * B[k][j]
99+
}
100+
}
101+
}
102+
return C
103+
}
104+
105+
// A is a square matrix
106+
const matrixExpo = (A, n) => {
107+
A = copyMatrix(A)
108+
if (n === 0) return Identity(A.length) // Identity matrix
109+
if (n === 1) return A
110+
111+
// Just like Binary exponentiation mentioned in ./BinaryExponentiationIterative.js
112+
let result = Identity(A.length)
113+
while (n > 0) {
114+
if (n % 2 !== 0) result = matrixMultiply(result, A)
115+
n = Math.floor(n / 2)
116+
if (n > 0) A = matrixMultiply(A, A)
117+
}
118+
return result
119+
}
120+
121+
const FibonacciMatrixExpo = (n) => {
122+
// F(0) = 0, F(1) = 1
123+
// F(n) = F(n-1) + F(n-2)
124+
// Consider below matrix multiplication:
125+
126+
// | F(n) | |1 1| |F(n-1)|
127+
// | | = | | * | |
128+
// |F(n-1)| |1 0| |F(n-2)|
129+
130+
// F(n, n-1) = pow(A, n-1) * F(1, 0)
131+
132+
if (n === 0) return 0
133+
134+
const A = [
135+
[1, 1],
136+
[1, 0]
137+
]
138+
const poweredA = matrixExpo(A, n - 1) // A raise to the power n
139+
let F = [
140+
[1],
141+
[0]
142+
]
143+
F = matrixMultiply(poweredA, F)
144+
return F[0][0]
145+
}
146+
72147
export { FibonacciDpWithoutRecursion }
73148
export { FibonacciIterative }
74149
export { FibonacciRecursive }
75150
export { FibonacciRecursiveDP }
151+
export { FibonacciMatrixExpo }

Maths/test/Fibonacci.test.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import {
22
FibonacciDpWithoutRecursion,
33
FibonacciRecursiveDP,
44
FibonacciIterative,
5-
FibonacciRecursive
5+
FibonacciRecursive,
6+
FibonacciMatrixExpo
67
} from '../Fibonacci'
78

89
describe('Fibonanci', () => {
@@ -27,4 +28,8 @@ describe('Fibonanci', () => {
2728
expect.arrayContaining([1, 1, 2, 3, 5])
2829
)
2930
})
31+
32+
it('should return number for FibonnaciMatrixExpo', () => {
33+
expect(FibonacciMatrixExpo(5)).toBe(5)
34+
})
3035
})

0 commit comments

Comments
 (0)