diff --git a/Maths/Fibonacci.js b/Maths/Fibonacci.js index c1b153f24d..fc8102ad2c 100644 --- a/Maths/Fibonacci.js +++ b/Maths/Fibonacci.js @@ -1,51 +1,67 @@ -const list = [] - -const FibonacciIterative = (nth) => { - const sequence = [] - - 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]) +// 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 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 } diff --git a/Maths/test/Fibonacci.test.js b/Maths/test/Fibonacci.test.js index bccd799be9..f3dcb98fe7 100644 --- a/Maths/test/Fibonacci.test.js +++ b/Maths/test/Fibonacci.test.js @@ -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) }) }) 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