mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-05 00:01:37 +08:00
Fibonacci.js
overhaul (#1049)
This commit is contained in:

committed by
GitHub

parent
95a8ec08ed
commit
1b64ba68fa
@ -1,51 +1,67 @@
|
||||
const list = []
|
||||
// https://en.wikipedia.org/wiki/Generalizations_of_Fibonacci_numbers#Extension_to_negative_integers
|
||||
const FibonacciIterative = (num) => {
|
||||
const isNeg = num < 0
|
||||
if (isNeg) num *= -1
|
||||
const sequence = [0]
|
||||
|
||||
const FibonacciIterative = (nth) => {
|
||||
const sequence = []
|
||||
if (num >= 1) sequence.push(1)
|
||||
if (num >= 2) sequence.push(isNeg ? -1 : 1)
|
||||
|
||||
if (nth >= 1) sequence.push(1)
|
||||
if (nth >= 2) sequence.push(1)
|
||||
|
||||
for (let i = 2; i < nth; i++) {
|
||||
sequence.push(sequence[i - 1] + sequence[i - 2])
|
||||
for (let i = 2; i < num; i++) {
|
||||
sequence.push(
|
||||
isNeg ? sequence[i - 1] - sequence[i] : sequence[i] + sequence[i - 1]
|
||||
)
|
||||
}
|
||||
|
||||
return sequence
|
||||
}
|
||||
|
||||
const FibonacciRecursive = (number) => {
|
||||
const FibonacciGenerator = function * (neg) {
|
||||
let a = 0
|
||||
let b = 1
|
||||
yield a
|
||||
while (true) {
|
||||
yield b;
|
||||
[a, b] = neg ? [b, a - b] : [b, a + b]
|
||||
}
|
||||
}
|
||||
|
||||
const list = []
|
||||
const FibonacciRecursive = (num) => {
|
||||
const isNeg = num < 0
|
||||
if (isNeg) num *= -1
|
||||
return (() => {
|
||||
switch (list.length) {
|
||||
case 0:
|
||||
list.push(1)
|
||||
return FibonacciRecursive(number)
|
||||
list.push(0)
|
||||
return FibonacciRecursive(num)
|
||||
case 1:
|
||||
list.push(1)
|
||||
return FibonacciRecursive(number)
|
||||
case number:
|
||||
return FibonacciRecursive(num)
|
||||
case num + 1:
|
||||
return list
|
||||
default:
|
||||
list.push(list[list.length - 1] + list[list.length - 2])
|
||||
return FibonacciRecursive(number)
|
||||
list.push(list.at(-1) + list.at(-2))
|
||||
return FibonacciRecursive(num)
|
||||
}
|
||||
})()
|
||||
})().map((fib, i) => fib * (isNeg ? (-1) ** (i + 1) : 1))
|
||||
}
|
||||
|
||||
const dict = new Map()
|
||||
|
||||
const FibonacciRecursiveDP = (stairs) => {
|
||||
if (stairs <= 0) return 0
|
||||
if (stairs === 1) return 1
|
||||
const isNeg = stairs < 0
|
||||
if (isNeg) stairs *= -1
|
||||
|
||||
if (stairs <= 1) return stairs
|
||||
|
||||
// Memoize stair count
|
||||
if (dict.has(stairs)) return dict.get(stairs)
|
||||
if (dict.has(stairs)) return (isNeg ? (-1) ** (stairs + 1) : 1) * dict.get(stairs)
|
||||
|
||||
const res =
|
||||
FibonacciRecursiveDP(stairs - 1) + FibonacciRecursiveDP(stairs - 2)
|
||||
const res = FibonacciRecursiveDP(stairs - 1) + FibonacciRecursiveDP(stairs - 2)
|
||||
|
||||
dict.set(stairs, res)
|
||||
|
||||
return res
|
||||
return (isNeg ? (-1) ** (stairs + 1) : 1) * res
|
||||
}
|
||||
|
||||
// Algorithms
|
||||
@ -59,12 +75,16 @@ const FibonacciRecursiveDP = (stairs) => {
|
||||
// a function of the number of input bits
|
||||
// @Satzyakiz
|
||||
|
||||
const FibonacciDpWithoutRecursion = (number) => {
|
||||
const table = []
|
||||
const FibonacciDpWithoutRecursion = (num) => {
|
||||
const isNeg = num < 0
|
||||
if (isNeg) num *= -1
|
||||
const table = [0]
|
||||
table.push(1)
|
||||
table.push(1)
|
||||
for (let i = 2; i < number; ++i) {
|
||||
table.push(table[i - 1] + table[i - 2])
|
||||
table.push(isNeg ? -1 : 1)
|
||||
for (let i = 2; i < num; ++i) {
|
||||
table.push(
|
||||
isNeg ? table[i - 1] - table[i] : table[i] + table[i - 1]
|
||||
)
|
||||
}
|
||||
return table
|
||||
}
|
||||
@ -76,24 +96,31 @@ const copyMatrix = (A) => {
|
||||
}
|
||||
|
||||
const Identity = (size) => {
|
||||
const isBigInt = typeof size === 'bigint'
|
||||
const ZERO = isBigInt ? 0n : 0
|
||||
const ONE = isBigInt ? 1n : 1
|
||||
size = Number(size)
|
||||
const I = Array(size).fill(null).map(() => Array(size).fill())
|
||||
return I.map((row, rowIdx) => row.map((_col, colIdx) => {
|
||||
return rowIdx === colIdx ? 1 : 0
|
||||
return rowIdx === colIdx ? ONE : ZERO
|
||||
}))
|
||||
}
|
||||
|
||||
// A of size (l x m) and B of size (m x n)
|
||||
// product C will be of size (l x n)
|
||||
// product C will be of size (l x n).
|
||||
// both matrices must have same-type numeric values
|
||||
// either both BigInt or both Number
|
||||
const matrixMultiply = (A, B) => {
|
||||
A = copyMatrix(A)
|
||||
B = copyMatrix(B)
|
||||
const isBigInt = typeof A[0][0] === 'bigint'
|
||||
const l = A.length
|
||||
const m = B.length
|
||||
const n = B[0].length // Assuming non-empty matrices
|
||||
const C = Array(l).fill(null).map(() => Array(n).fill())
|
||||
for (let i = 0; i < l; i++) {
|
||||
for (let j = 0; j < n; j++) {
|
||||
C[i][j] = 0
|
||||
C[i][j] = isBigInt ? 0n : 0
|
||||
for (let k = 0; k < m; k++) {
|
||||
C[i][j] += A[i][k] * B[k][j]
|
||||
}
|
||||
@ -110,18 +137,25 @@ const matrixMultiply = (A, B) => {
|
||||
// A is a square matrix
|
||||
const matrixExpo = (A, n) => {
|
||||
A = copyMatrix(A)
|
||||
const isBigInt = typeof n === 'bigint'
|
||||
const ZERO = isBigInt ? 0n : 0
|
||||
const TWO = isBigInt ? 2n : 2
|
||||
|
||||
// Just like Binary exponentiation mentioned in ./BinaryExponentiationIterative.js
|
||||
let result = Identity(A.length) // Identity matrix
|
||||
while (n > 0) {
|
||||
if (n % 2 !== 0) result = matrixMultiply(result, A)
|
||||
n = Math.floor(n / 2)
|
||||
if (n > 0) A = matrixMultiply(A, A)
|
||||
let result = Identity((isBigInt ? BigInt : Number)(A.length)) // Identity matrix
|
||||
while (n > ZERO) {
|
||||
if (n % TWO !== ZERO) result = matrixMultiply(result, A)
|
||||
n /= TWO
|
||||
if (!isBigInt) n = Math.floor(n)
|
||||
if (n > ZERO) A = matrixMultiply(A, A)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const FibonacciMatrixExpo = (n) => {
|
||||
const FibonacciMatrixExpo = (num) => {
|
||||
const isBigInt = typeof num === 'bigint'
|
||||
const ZERO = isBigInt ? 0n : 0
|
||||
const ONE = isBigInt ? 1n : 1
|
||||
// F(0) = 0, F(1) = 1
|
||||
// F(n) = F(n-1) + F(n-2)
|
||||
// Consider below matrix multiplication:
|
||||
@ -134,23 +168,28 @@ const FibonacciMatrixExpo = (n) => {
|
||||
// or F(n, n-1) = A * A * F(n-2, n-3)
|
||||
// or F(n, n-1) = pow(A, n-1) * F(1, 0)
|
||||
|
||||
if (n === 0) return 0
|
||||
if (num === ZERO) return num
|
||||
|
||||
const isNeg = num < 0
|
||||
if (isNeg) num *= -ONE
|
||||
|
||||
const A = [
|
||||
[1, 1],
|
||||
[1, 0]
|
||||
[ONE, ONE],
|
||||
[ONE, ZERO]
|
||||
]
|
||||
const poweredA = matrixExpo(A, n - 1) // A raised to the power n-1
|
||||
|
||||
const poweredA = matrixExpo(A, num - ONE) // A raised to the power n-1
|
||||
let F = [
|
||||
[1],
|
||||
[0]
|
||||
[ONE],
|
||||
[ZERO]
|
||||
]
|
||||
F = matrixMultiply(poweredA, F)
|
||||
return F[0][0]
|
||||
return F[0][0] * (isNeg ? (-ONE) ** (num + ONE) : ONE)
|
||||
}
|
||||
|
||||
export { FibonacciDpWithoutRecursion }
|
||||
export { FibonacciIterative }
|
||||
export { FibonacciGenerator }
|
||||
export { FibonacciRecursive }
|
||||
export { FibonacciRecursiveDP }
|
||||
export { FibonacciMatrixExpo }
|
||||
|
@ -2,30 +2,61 @@ import {
|
||||
FibonacciDpWithoutRecursion,
|
||||
FibonacciRecursiveDP,
|
||||
FibonacciIterative,
|
||||
FibonacciGenerator,
|
||||
FibonacciRecursive,
|
||||
FibonacciMatrixExpo
|
||||
} from '../Fibonacci'
|
||||
|
||||
describe('Fibonacci', () => {
|
||||
it('should return an array of numbers for FibonacciIterative', () => {
|
||||
expect(FibonacciIterative(5)).toEqual(
|
||||
expect.arrayContaining([1, 1, 2, 3, 5])
|
||||
expect(FibonacciIterative(6)).toEqual(
|
||||
expect.arrayContaining([0, 1, 1, 2, 3, 5, 8])
|
||||
)
|
||||
expect(FibonacciIterative(-6)).toEqual(
|
||||
expect.arrayContaining([0, 1, -1, 2, -3, 5, -8])
|
||||
)
|
||||
})
|
||||
|
||||
it('should return number for FibonacciGenerator', () => {
|
||||
const positive = FibonacciGenerator()
|
||||
expect(positive.next().value).toBe(0)
|
||||
expect(positive.next().value).toBe(1)
|
||||
expect(positive.next().value).toBe(1)
|
||||
expect(positive.next().value).toBe(2)
|
||||
expect(positive.next().value).toBe(3)
|
||||
expect(positive.next().value).toBe(5)
|
||||
expect(positive.next().value).toBe(8)
|
||||
|
||||
const negative = FibonacciGenerator(true)
|
||||
expect(negative.next().value).toBe(0)
|
||||
expect(negative.next().value).toBe(1)
|
||||
expect(negative.next().value).toBe(-1)
|
||||
expect(negative.next().value).toBe(2)
|
||||
expect(negative.next().value).toBe(-3)
|
||||
expect(negative.next().value).toBe(5)
|
||||
expect(negative.next().value).toBe(-8)
|
||||
})
|
||||
|
||||
it('should return an array of numbers for FibonacciRecursive', () => {
|
||||
expect(FibonacciRecursive(5)).toEqual(
|
||||
expect.arrayContaining([1, 1, 2, 3, 5])
|
||||
expect(FibonacciRecursive(6)).toEqual(
|
||||
expect.arrayContaining([0, 1, 1, 2, 3, 5, 8])
|
||||
)
|
||||
expect(FibonacciRecursive(-6)).toEqual(
|
||||
expect.arrayContaining([-0, 1, -1, 2, -3, 5, -8])
|
||||
)
|
||||
})
|
||||
|
||||
it('should return number for FibonacciRecursiveDP', () => {
|
||||
expect(FibonacciRecursiveDP(5)).toBe(5)
|
||||
expect(FibonacciRecursiveDP(6)).toBe(8)
|
||||
expect(FibonacciRecursiveDP(-6)).toBe(-8)
|
||||
})
|
||||
|
||||
it('should return an array of numbers for FibonacciDpWithoutRecursion', () => {
|
||||
expect(FibonacciDpWithoutRecursion(5)).toEqual(
|
||||
expect.arrayContaining([1, 1, 2, 3, 5])
|
||||
expect(FibonacciDpWithoutRecursion(6)).toEqual(
|
||||
expect.arrayContaining([0, 1, 1, 2, 3, 5, 8])
|
||||
)
|
||||
expect(FibonacciDpWithoutRecursion(-6)).toEqual(
|
||||
expect.arrayContaining([0, 1, -1, 2, -3, 5, -8])
|
||||
)
|
||||
})
|
||||
|
||||
@ -36,5 +67,32 @@ describe('Fibonacci', () => {
|
||||
expect(FibonacciMatrixExpo(3)).toBe(2)
|
||||
expect(FibonacciMatrixExpo(4)).toBe(3)
|
||||
expect(FibonacciMatrixExpo(5)).toBe(5)
|
||||
expect(FibonacciMatrixExpo(6)).toBe(8)
|
||||
|
||||
expect(FibonacciMatrixExpo(-0)).toBe(-0)
|
||||
expect(FibonacciMatrixExpo(-1)).toBe(1)
|
||||
expect(FibonacciMatrixExpo(-2)).toBe(-1)
|
||||
expect(FibonacciMatrixExpo(-3)).toBe(2)
|
||||
expect(FibonacciMatrixExpo(-4)).toBe(-3)
|
||||
expect(FibonacciMatrixExpo(-5)).toBe(5)
|
||||
expect(FibonacciMatrixExpo(-6)).toBe(-8)
|
||||
})
|
||||
|
||||
it('should return bigint for FibonacciMatrixExpo', () => {
|
||||
expect(FibonacciMatrixExpo(0n)).toBe(0n)
|
||||
expect(FibonacciMatrixExpo(1n)).toBe(1n)
|
||||
expect(FibonacciMatrixExpo(2n)).toBe(1n)
|
||||
expect(FibonacciMatrixExpo(3n)).toBe(2n)
|
||||
expect(FibonacciMatrixExpo(4n)).toBe(3n)
|
||||
expect(FibonacciMatrixExpo(5n)).toBe(5n)
|
||||
expect(FibonacciMatrixExpo(6n)).toBe(8n)
|
||||
|
||||
expect(FibonacciMatrixExpo(-0n)).toBe(0n)
|
||||
expect(FibonacciMatrixExpo(-1n)).toBe(1n)
|
||||
expect(FibonacciMatrixExpo(-2n)).toBe(-1n)
|
||||
expect(FibonacciMatrixExpo(-3n)).toBe(2n)
|
||||
expect(FibonacciMatrixExpo(-4n)).toBe(-3n)
|
||||
expect(FibonacciMatrixExpo(-5n)).toBe(5n)
|
||||
expect(FibonacciMatrixExpo(-6n)).toBe(-8n)
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user