Skip to content

Commit 1b64ba6

Browse files
authored
Fibonacci.js overhaul (TheAlgorithms#1049)
1 parent 95a8ec0 commit 1b64ba6

File tree

2 files changed

+150
-53
lines changed

2 files changed

+150
-53
lines changed

Maths/Fibonacci.js

Lines changed: 85 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,67 @@
1-
const list = []
2-
3-
const FibonacciIterative = (nth) => {
4-
const sequence = []
5-
6-
if (nth >= 1) sequence.push(1)
7-
if (nth >= 2) sequence.push(1)
8-
9-
for (let i = 2; i < nth; i++) {
10-
sequence.push(sequence[i - 1] + sequence[i - 2])
1+
// https://en.wikipedia.org/wiki/Generalizations_of_Fibonacci_numbers#Extension_to_negative_integers
2+
const FibonacciIterative = (num) => {
3+
const isNeg = num < 0
4+
if (isNeg) num *= -1
5+
const sequence = [0]
6+
7+
if (num >= 1) sequence.push(1)
8+
if (num >= 2) sequence.push(isNeg ? -1 : 1)
9+
10+
for (let i = 2; i < num; i++) {
11+
sequence.push(
12+
isNeg ? sequence[i - 1] - sequence[i] : sequence[i] + sequence[i - 1]
13+
)
1114
}
1215

1316
return sequence
1417
}
1518

16-
const FibonacciRecursive = (number) => {
19+
const FibonacciGenerator = function * (neg) {
20+
let a = 0
21+
let b = 1
22+
yield a
23+
while (true) {
24+
yield b;
25+
[a, b] = neg ? [b, a - b] : [b, a + b]
26+
}
27+
}
28+
29+
const list = []
30+
const FibonacciRecursive = (num) => {
31+
const isNeg = num < 0
32+
if (isNeg) num *= -1
1733
return (() => {
1834
switch (list.length) {
1935
case 0:
20-
list.push(1)
21-
return FibonacciRecursive(number)
36+
list.push(0)
37+
return FibonacciRecursive(num)
2238
case 1:
2339
list.push(1)
24-
return FibonacciRecursive(number)
25-
case number:
40+
return FibonacciRecursive(num)
41+
case num + 1:
2642
return list
2743
default:
28-
list.push(list[list.length - 1] + list[list.length - 2])
29-
return FibonacciRecursive(number)
44+
list.push(list.at(-1) + list.at(-2))
45+
return FibonacciRecursive(num)
3046
}
31-
})()
47+
})().map((fib, i) => fib * (isNeg ? (-1) ** (i + 1) : 1))
3248
}
3349

3450
const dict = new Map()
35-
3651
const FibonacciRecursiveDP = (stairs) => {
37-
if (stairs <= 0) return 0
38-
if (stairs === 1) return 1
52+
const isNeg = stairs < 0
53+
if (isNeg) stairs *= -1
54+
55+
if (stairs <= 1) return stairs
3956

4057
// Memoize stair count
41-
if (dict.has(stairs)) return dict.get(stairs)
58+
if (dict.has(stairs)) return (isNeg ? (-1) ** (stairs + 1) : 1) * dict.get(stairs)
4259

43-
const res =
44-
FibonacciRecursiveDP(stairs - 1) + FibonacciRecursiveDP(stairs - 2)
60+
const res = FibonacciRecursiveDP(stairs - 1) + FibonacciRecursiveDP(stairs - 2)
4561

4662
dict.set(stairs, res)
4763

48-
return res
64+
return (isNeg ? (-1) ** (stairs + 1) : 1) * res
4965
}
5066

5167
// Algorithms
@@ -59,12 +75,16 @@ const FibonacciRecursiveDP = (stairs) => {
5975
// a function of the number of input bits
6076
// @Satzyakiz
6177

62-
const FibonacciDpWithoutRecursion = (number) => {
63-
const table = []
78+
const FibonacciDpWithoutRecursion = (num) => {
79+
const isNeg = num < 0
80+
if (isNeg) num *= -1
81+
const table = [0]
6482
table.push(1)
65-
table.push(1)
66-
for (let i = 2; i < number; ++i) {
67-
table.push(table[i - 1] + table[i - 2])
83+
table.push(isNeg ? -1 : 1)
84+
for (let i = 2; i < num; ++i) {
85+
table.push(
86+
isNeg ? table[i - 1] - table[i] : table[i] + table[i - 1]
87+
)
6888
}
6989
return table
7090
}
@@ -76,24 +96,31 @@ const copyMatrix = (A) => {
7696
}
7797

7898
const Identity = (size) => {
99+
const isBigInt = typeof size === 'bigint'
100+
const ZERO = isBigInt ? 0n : 0
101+
const ONE = isBigInt ? 1n : 1
102+
size = Number(size)
79103
const I = Array(size).fill(null).map(() => Array(size).fill())
80104
return I.map((row, rowIdx) => row.map((_col, colIdx) => {
81-
return rowIdx === colIdx ? 1 : 0
105+
return rowIdx === colIdx ? ONE : ZERO
82106
}))
83107
}
84108

85109
// A of size (l x m) and B of size (m x n)
86-
// product C will be of size (l x n)
110+
// product C will be of size (l x n).
111+
// both matrices must have same-type numeric values
112+
// either both BigInt or both Number
87113
const matrixMultiply = (A, B) => {
88114
A = copyMatrix(A)
89115
B = copyMatrix(B)
116+
const isBigInt = typeof A[0][0] === 'bigint'
90117
const l = A.length
91118
const m = B.length
92119
const n = B[0].length // Assuming non-empty matrices
93120
const C = Array(l).fill(null).map(() => Array(n).fill())
94121
for (let i = 0; i < l; i++) {
95122
for (let j = 0; j < n; j++) {
96-
C[i][j] = 0
123+
C[i][j] = isBigInt ? 0n : 0
97124
for (let k = 0; k < m; k++) {
98125
C[i][j] += A[i][k] * B[k][j]
99126
}
@@ -110,18 +137,25 @@ const matrixMultiply = (A, B) => {
110137
// A is a square matrix
111138
const matrixExpo = (A, n) => {
112139
A = copyMatrix(A)
140+
const isBigInt = typeof n === 'bigint'
141+
const ZERO = isBigInt ? 0n : 0
142+
const TWO = isBigInt ? 2n : 2
113143

114144
// Just like Binary exponentiation mentioned in ./BinaryExponentiationIterative.js
115-
let result = Identity(A.length) // Identity matrix
116-
while (n > 0) {
117-
if (n % 2 !== 0) result = matrixMultiply(result, A)
118-
n = Math.floor(n / 2)
119-
if (n > 0) A = matrixMultiply(A, A)
145+
let result = Identity((isBigInt ? BigInt : Number)(A.length)) // Identity matrix
146+
while (n > ZERO) {
147+
if (n % TWO !== ZERO) result = matrixMultiply(result, A)
148+
n /= TWO
149+
if (!isBigInt) n = Math.floor(n)
150+
if (n > ZERO) A = matrixMultiply(A, A)
120151
}
121152
return result
122153
}
123154

124-
const FibonacciMatrixExpo = (n) => {
155+
const FibonacciMatrixExpo = (num) => {
156+
const isBigInt = typeof num === 'bigint'
157+
const ZERO = isBigInt ? 0n : 0
158+
const ONE = isBigInt ? 1n : 1
125159
// F(0) = 0, F(1) = 1
126160
// F(n) = F(n-1) + F(n-2)
127161
// Consider below matrix multiplication:
@@ -134,23 +168,28 @@ const FibonacciMatrixExpo = (n) => {
134168
// or F(n, n-1) = A * A * F(n-2, n-3)
135169
// or F(n, n-1) = pow(A, n-1) * F(1, 0)
136170

137-
if (n === 0) return 0
171+
if (num === ZERO) return num
172+
173+
const isNeg = num < 0
174+
if (isNeg) num *= -ONE
138175

139176
const A = [
140-
[1, 1],
141-
[1, 0]
177+
[ONE, ONE],
178+
[ONE, ZERO]
142179
]
143-
const poweredA = matrixExpo(A, n - 1) // A raised to the power n-1
180+
181+
const poweredA = matrixExpo(A, num - ONE) // A raised to the power n-1
144182
let F = [
145-
[1],
146-
[0]
183+
[ONE],
184+
[ZERO]
147185
]
148186
F = matrixMultiply(poweredA, F)
149-
return F[0][0]
187+
return F[0][0] * (isNeg ? (-ONE) ** (num + ONE) : ONE)
150188
}
151189

152190
export { FibonacciDpWithoutRecursion }
153191
export { FibonacciIterative }
192+
export { FibonacciGenerator }
154193
export { FibonacciRecursive }
155194
export { FibonacciRecursiveDP }
156195
export { FibonacciMatrixExpo }

Maths/test/Fibonacci.test.js

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,61 @@ import {
22
FibonacciDpWithoutRecursion,
33
FibonacciRecursiveDP,
44
FibonacciIterative,
5+
FibonacciGenerator,
56
FibonacciRecursive,
67
FibonacciMatrixExpo
78
} from '../Fibonacci'
89

910
describe('Fibonacci', () => {
1011
it('should return an array of numbers for FibonacciIterative', () => {
11-
expect(FibonacciIterative(5)).toEqual(
12-
expect.arrayContaining([1, 1, 2, 3, 5])
12+
expect(FibonacciIterative(6)).toEqual(
13+
expect.arrayContaining([0, 1, 1, 2, 3, 5, 8])
14+
)
15+
expect(FibonacciIterative(-6)).toEqual(
16+
expect.arrayContaining([0, 1, -1, 2, -3, 5, -8])
1317
)
1418
})
1519

20+
it('should return number for FibonacciGenerator', () => {
21+
const positive = FibonacciGenerator()
22+
expect(positive.next().value).toBe(0)
23+
expect(positive.next().value).toBe(1)
24+
expect(positive.next().value).toBe(1)
25+
expect(positive.next().value).toBe(2)
26+
expect(positive.next().value).toBe(3)
27+
expect(positive.next().value).toBe(5)
28+
expect(positive.next().value).toBe(8)
29+
30+
const negative = FibonacciGenerator(true)
31+
expect(negative.next().value).toBe(0)
32+
expect(negative.next().value).toBe(1)
33+
expect(negative.next().value).toBe(-1)
34+
expect(negative.next().value).toBe(2)
35+
expect(negative.next().value).toBe(-3)
36+
expect(negative.next().value).toBe(5)
37+
expect(negative.next().value).toBe(-8)
38+
})
39+
1640
it('should return an array of numbers for FibonacciRecursive', () => {
17-
expect(FibonacciRecursive(5)).toEqual(
18-
expect.arrayContaining([1, 1, 2, 3, 5])
41+
expect(FibonacciRecursive(6)).toEqual(
42+
expect.arrayContaining([0, 1, 1, 2, 3, 5, 8])
43+
)
44+
expect(FibonacciRecursive(-6)).toEqual(
45+
expect.arrayContaining([-0, 1, -1, 2, -3, 5, -8])
1946
)
2047
})
2148

2249
it('should return number for FibonacciRecursiveDP', () => {
23-
expect(FibonacciRecursiveDP(5)).toBe(5)
50+
expect(FibonacciRecursiveDP(6)).toBe(8)
51+
expect(FibonacciRecursiveDP(-6)).toBe(-8)
2452
})
2553

2654
it('should return an array of numbers for FibonacciDpWithoutRecursion', () => {
27-
expect(FibonacciDpWithoutRecursion(5)).toEqual(
28-
expect.arrayContaining([1, 1, 2, 3, 5])
55+
expect(FibonacciDpWithoutRecursion(6)).toEqual(
56+
expect.arrayContaining([0, 1, 1, 2, 3, 5, 8])
57+
)
58+
expect(FibonacciDpWithoutRecursion(-6)).toEqual(
59+
expect.arrayContaining([0, 1, -1, 2, -3, 5, -8])
2960
)
3061
})
3162

@@ -36,5 +67,32 @@ describe('Fibonacci', () => {
3667
expect(FibonacciMatrixExpo(3)).toBe(2)
3768
expect(FibonacciMatrixExpo(4)).toBe(3)
3869
expect(FibonacciMatrixExpo(5)).toBe(5)
70+
expect(FibonacciMatrixExpo(6)).toBe(8)
71+
72+
expect(FibonacciMatrixExpo(-0)).toBe(-0)
73+
expect(FibonacciMatrixExpo(-1)).toBe(1)
74+
expect(FibonacciMatrixExpo(-2)).toBe(-1)
75+
expect(FibonacciMatrixExpo(-3)).toBe(2)
76+
expect(FibonacciMatrixExpo(-4)).toBe(-3)
77+
expect(FibonacciMatrixExpo(-5)).toBe(5)
78+
expect(FibonacciMatrixExpo(-6)).toBe(-8)
79+
})
80+
81+
it('should return bigint for FibonacciMatrixExpo', () => {
82+
expect(FibonacciMatrixExpo(0n)).toBe(0n)
83+
expect(FibonacciMatrixExpo(1n)).toBe(1n)
84+
expect(FibonacciMatrixExpo(2n)).toBe(1n)
85+
expect(FibonacciMatrixExpo(3n)).toBe(2n)
86+
expect(FibonacciMatrixExpo(4n)).toBe(3n)
87+
expect(FibonacciMatrixExpo(5n)).toBe(5n)
88+
expect(FibonacciMatrixExpo(6n)).toBe(8n)
89+
90+
expect(FibonacciMatrixExpo(-0n)).toBe(0n)
91+
expect(FibonacciMatrixExpo(-1n)).toBe(1n)
92+
expect(FibonacciMatrixExpo(-2n)).toBe(-1n)
93+
expect(FibonacciMatrixExpo(-3n)).toBe(2n)
94+
expect(FibonacciMatrixExpo(-4n)).toBe(-3n)
95+
expect(FibonacciMatrixExpo(-5n)).toBe(5n)
96+
expect(FibonacciMatrixExpo(-6n)).toBe(-8n)
3997
})
4098
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy