mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-04 07:29:47 +08:00

* chore: Switch to Node 20 + Vitest * chore: migrate to vitest mock functions * chore: code style (switch to prettier) * test: re-enable long-running test Seems the switch to Node 20 and Vitest has vastly improved the code's and / or the test's runtime! see #1193 * chore: code style * chore: fix failing tests * Updated Documentation in README.md * Update contribution guidelines to state usage of Prettier * fix: set prettier printWidth back to 80 * chore: apply updated code style automatically * fix: set prettier line endings to lf again * chore: apply updated code style automatically --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
210 lines
5.4 KiB
JavaScript
210 lines
5.4 KiB
JavaScript
// 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]
|
|
|
|
if (num >= 1) sequence.push(1)
|
|
if (num >= 2) sequence.push(isNeg ? -1 : 1)
|
|
|
|
for (let i = 2; i < num; i++) {
|
|
sequence.push(
|
|
isNeg ? sequence[i - 1] - sequence[i] : sequence[i] + sequence[i - 1]
|
|
)
|
|
}
|
|
|
|
return sequence
|
|
}
|
|
|
|
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(0)
|
|
return FibonacciRecursive(num)
|
|
case 1:
|
|
list.push(1)
|
|
return FibonacciRecursive(num)
|
|
case num + 1:
|
|
return list
|
|
default:
|
|
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) => {
|
|
const isNeg = stairs < 0
|
|
if (isNeg) stairs *= -1
|
|
|
|
if (stairs <= 1) return stairs
|
|
|
|
// Memoize stair count
|
|
if (dict.has(stairs))
|
|
return (isNeg ? (-1) ** (stairs + 1) : 1) * dict.get(stairs)
|
|
|
|
const res =
|
|
FibonacciRecursiveDP(stairs - 1) + FibonacciRecursiveDP(stairs - 2)
|
|
|
|
dict.set(stairs, res)
|
|
|
|
return (isNeg ? (-1) ** (stairs + 1) : 1) * res
|
|
}
|
|
|
|
// Algorithms
|
|
// Calculates Fibonacci(n) such that Fibonacci(n) = Fibonacci(n - 1) + Fibonacci(n - 2)
|
|
// Fibonacci(0) = Fibonacci(1) = 1
|
|
// Uses a bottom up dynamic programming approach
|
|
// Solve each sub-problem once, using results of previous sub-problems
|
|
// which are n-1 and n-2 for Fibonacci numbers
|
|
// Although this algorithm is linear in space and time as a function
|
|
// of the input value n, it is exponential in the size of n as
|
|
// a function of the number of input bits
|
|
// @Satzyakiz
|
|
|
|
const FibonacciDpWithoutRecursion = (num) => {
|
|
const isNeg = num < 0
|
|
if (isNeg) num *= -1
|
|
const table = [0]
|
|
table.push(1)
|
|
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
|
|
}
|
|
|
|
// Using Matrix exponentiation to find n-th fibonacci in O(log n) time
|
|
|
|
const copyMatrix = (A) => {
|
|
return A.map((row) => row.map((cell) => cell))
|
|
}
|
|
|
|
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 ? ONE : ZERO
|
|
})
|
|
)
|
|
}
|
|
|
|
// A of size (l x m) and B of size (m 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] = isBigInt ? 0n : 0
|
|
for (let k = 0; k < m; k++) {
|
|
C[i][j] += A[i][k] * B[k][j]
|
|
}
|
|
}
|
|
}
|
|
return C
|
|
}
|
|
|
|
/**
|
|
* Computes A raised to the power n i.e. pow(A, n) where A is a square matrix
|
|
* @param {*} A the square matrix
|
|
* @param {*} n the exponent
|
|
*/
|
|
// 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((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 = (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:
|
|
|
|
// | F(n) | |1 1| |F(n-1)|
|
|
// | | = | | * | |
|
|
// |F(n-1)| |1 0| |F(n-2)|
|
|
|
|
// Let's rewrite it as F(n, n-1) = A * F(n-1, n-2)
|
|
// 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 (num === ZERO) return num
|
|
|
|
const isNeg = num < 0
|
|
if (isNeg) num *= -ONE
|
|
|
|
const A = [
|
|
[ONE, ONE],
|
|
[ONE, ZERO]
|
|
]
|
|
|
|
const poweredA = matrixExpo(A, num - ONE) // A raised to the power n-1
|
|
let F = [[ONE], [ZERO]]
|
|
F = matrixMultiply(poweredA, F)
|
|
return F[0][0] * (isNeg ? (-ONE) ** (num + ONE) : ONE)
|
|
}
|
|
|
|
/*
|
|
Resource : https://math.hmc.edu/funfacts/fibonacci-number-formula/
|
|
*/
|
|
|
|
const sqrt5 = Math.sqrt(5)
|
|
const phi = (1 + sqrt5) / 2
|
|
const psi = (1 - sqrt5) / 2
|
|
|
|
const FibonacciUsingFormula = (n) => Math.round((phi ** n - psi ** n) / sqrt5)
|
|
|
|
export { FibonacciDpWithoutRecursion }
|
|
export { FibonacciIterative }
|
|
export { FibonacciGenerator }
|
|
export { FibonacciRecursive }
|
|
export { FibonacciRecursiveDP }
|
|
export { FibonacciMatrixExpo }
|
|
export { FibonacciUsingFormula }
|