Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fibonacci.js overhaul #1049

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
raklaptudirm merged 25 commits into TheAlgorithms:master from Rudxain:patch-2
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
398854c
Added BigInt support to `FibonacciMatrixExpo`
Rudxain Jun 21, 2022
ea39661
Fixed style
Rudxain Jun 21, 2022
33df079
Added negatives support to every fn
Rudxain Jun 21, 2022
f4f1d53
Fixed bug
Rudxain Jun 21, 2022
2bcbc58
Added all missing tests
Rudxain Jun 21, 2022
3d865f3
Splitted `BigInt` & `Number` tests
Rudxain Jun 21, 2022
0dc81a8
Shortened `sign` to `sgn` in most (not all) places
Rudxain Jun 21, 2022
1f61f51
Fixed indexing
Rudxain Jun 21, 2022
7090d5a
Fixed bug at FDPWR
Rudxain Jun 21, 2022
03ed1f8
Fixed FRDP
Rudxain Jun 21, 2022
b2101fc
Fixed F iter
Rudxain Jun 21, 2022
c5fe0b5
Fixed FRDP again
Rudxain Jun 21, 2022
787fd75
Added `FibonacciGenerator`
Rudxain Jun 21, 2022
c11856b
Added tests for `FibonacciGenerator`
Rudxain Jun 21, 2022
028ebe9
More consistent test
Rudxain Jun 21, 2022
69bbe51
Fixed FRDP yet again
Rudxain Jun 21, 2022
6f0de19
Fixed Fib recursive
Rudxain Jun 21, 2022
82861ef
Explicitly using negative 0
Rudxain Jun 21, 2022
83afc56
Fixed errors
Rudxain Jun 21, 2022
a034743
Fixed part of style
Rudxain Jun 21, 2022
cdbf5ff
Fixed style
Rudxain Jun 21, 2022
79d4c47
Ran `npx standard --fix Fibonacci.js`
Rudxain Jun 22, 2022
42d3ae4
Removed trailing space
Rudxain Jun 22, 2022
21ff3fd
Update Fibonacci.js
Rudxain Jun 26, 2022
81e64a4
Update Fibonacci.js
Rudxain Jun 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 85 additions & 46 deletions Maths/Fibonacci.js
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
}
Expand All @@ -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]
}
Expand All @@ -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:
Expand All @@ -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 }
72 changes: 65 additions & 7 deletions Maths/test/Fibonacci.test.js
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -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])
)
})

Expand All @@ -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)
})
})

AltStyle によって変換されたページ (->オリジナル) /