Number Sequence Generator: From Fibonacci to Primes – Algorithm Implementations#

I was tackling a Fibonacci problem on LeetCode recently. My recursive solution timed out immediately. Looking for an online tool to verify my optimized approach, I found either single-purpose tools or ad-bloated pages. So I built my own generator supporting multiple sequence types, and here’s what I learned along the way.

Nine Common Mathematical Sequences#

Let’s start with the basics:

Sequence Type First Terms Formula
Fibonacci 0, 1, 1, 2, 3, 5… F(n) = F(n-1) + F(n-2)
Prime 2, 3, 5, 7, 11… No simple formula
Arithmetic 1, 4, 7, 10… a(n) = a(1) + (n-1)d
Geometric 2, 6, 18, 54… a(n) = a(1) × r^(n-1)
Squares 1, 4, 9, 16, 25… a(n) = n²
Cubes 1, 8, 27, 64… a(n) = n³
Triangular 1, 3, 6, 10, 15… a(n) = n(n+1)/2
Factorials 1, 2, 6, 24, 120… a(n) = n!
Powers of 2 1, 2, 4, 8, 16… a(n) = 2^n

Fibonacci: From Recursion to Iteration#

The most intuitive recursive approach:

function fib(n) {
  if (n <= 1) return n
  return fib(n - 1) + fib(n - 2)
}

This has O(2^n) time complexity. Computing fib(50) basically freezes. The problem is redundant calculations—fib(5) computes fib(3) twice, fib(4) computes fib(3) three times.

Optimization 1: Iteration

function fibonacci(n: number): number[] {
  const result = [0, 1]
  for (let i = 2; i < n; i++) {
    result[i] = result[i - 1] + result[i - 2]
  }
  return result
}

Time complexity O(n), space complexity O(n). If you only need the nth term, space drops to O(1):

function fibNth(n: number): number {
  if (n <= 1) return n
  let a = 0, b = 1
  for (let i = 2; i <= n; i++) {
    [a, b] = [b, a + b]
  }
  return b
}

Optimization 2: Matrix Exponentiation

function fibMatrix(n: number): number {
  if (n <= 1) return n

  function multiply(a: number[][], b: number[][]): number[][] {
    return [
      [a[0][0] * b[0][0] + a[0][1] * b[1][0], a[0][0] * b[0][1] + a[0][1] * b[1][1]],
      [a[1][0] * b[0][0] + a[1][1] * b[1][0], a[1][0] * b[0][1] + a[1][1] * b[1][1]]
    ]
  }

  function power(m: number[][], p: number): number[][] {
    if (p === 1) return m
    const half = power(m, Math.floor(p / 2))
    return p % 2 === 0 ? multiply(half, half) : multiply(multiply(half, half), m)
  }

  const base = [[1, 1], [1, 0]]
  return power(base, n - 1)[0][0]
}

Matrix exponentiation gives O(log n) time complexity. Computing fib(10^18) becomes trivial. The math behind it:

[F(n+1), F(n)  ]   [1, 1]^n
[F(n),   F(n-1)] = [1, 0]

Prime Generation: Trial Division vs Sieve of Eratosthenes#

The naive trial division approach:

function isPrime(n: number): boolean {
  if (n < 2) return false
  for (let i = 2; i <= Math.sqrt(n); i++) {
    if (n % i === 0) return false
  }
  return true
}

function generatePrimes(count: number): number[] {
  const primes: number[] = []
  let num = 2
  while (primes.length < count) {
    if (isPrime(num)) primes.push(num)
    num++
  }
  return primes
}

Trial division for a single prime is O(√n). Generating n primes is roughly O(n√n / log n).

For generating consecutive primes, the Sieve of Eratosthenes is far more efficient:

function sieveOfEratosthenes(limit: number): number[] {
  const isPrime = new Array(limit + 1).fill(true)
  isPrime[0] = isPrime[1] = false

  for (let i = 2; i * i <= limit; i++) {
    if (isPrime[i]) {
      for (let j = i * i; j <= limit; j += i) {
        isPrime[j] = false
      }
    }
  }

  return isPrime.reduce((primes: number[], prime, i) => {
    if (prime) primes.push(i)
    return primes
  }, [])
}

The sieve runs in O(n log log n) time. Generating primes under 1 million takes just milliseconds.

Key optimizations:

  1. Outer loop only needs to reach √limit
  2. Inner loop starts at i² because i×2, i×3… were already sieved by smaller primes

Arithmetic and Geometric Sequences: Parameterized Implementation#

These sequences shine with configurable parameters:

function arithmeticSequence(start: number, step: number, count: number): number[] {
  const result = []
  for (let i = 0; i < count; i++) {
    result.push(start + i * step)
  }
  return result
}

function geometricSequence(start: number, ratio: number, count: number): number[] {
  const result = []
  for (let i = 0; i < count; i++) {
    result.push(start * Math.pow(ratio, i))
  }
  return result
}

Edge cases:

geometricSequence(5, 0, 10)   // [5, 0, 0, 0, ...]
geometricSequence(1, -2, 10)  // [1, -2, 4, -8, 16, ...]
arithmeticSequence(5, 0, 10)  // [5, 5, 5, 5, ...]

Triangular Numbers and Factorials: From Formula to Code#

Triangular numbers follow the elegant formula n(n+1)/2:

function triangularNumbers(count: number): number[] {
  const result = []
  for (let i = 1; i <= count; i++) {
    result.push((i * (i + 1)) / 2)
  }
  return result
}

A neat property: the nth triangular number equals the sum of the first n natural numbers. Gauss’s formula confirms:

1 + 2 + 3 + ... + n = n(n+1)/2

Iterative factorial:

function factorials(count: number): number[] {
  const result = []
  let fact = 1
  for (let i = 1; i <= count; i++) {
    fact *= i
    result.push(fact)
  }
  return result
}

Factorials grow extremely fast. 20! = 2.43 × 10^18 is already near JavaScript’s safe integer limit. For larger values, use BigInt:

function factorialBigInt(n: number): bigint {
  let result = 1n
  for (let i = 2n; i <= BigInt(n); i++) {
    result *= i
  }
  return result
}

Real-World Applications#

These sequences aren’t just math exercises:

  • Fibonacci: Technical stock analysis, algorithm complexity, dynamic programming intro
  • Primes: RSA encryption, hash table sizing, random number generators
  • Arithmetic/Geometric: Compound interest, loan amortization, tiered pricing
  • Factorials: Permutations and combinations, probability, Taylor series

Handling Large Numbers#

JavaScript’s Number.MAX_SAFE_INTEGER is 2^53 - 1 = 9007199254740991. Beyond this, precision is lost:

console.log(9007199254740992 === 9007199254740993)  // true, precision lost

Check for overflow when generating sequences:

function safeGenerate(type: SequenceType, count: number): number[] | null {
  const result = generateSequence(type, count)
  const maxSafe = Number.MAX_SAFE_INTEGER

  for (const num of result) {
    if (num > maxSafe || num < -maxSafe) {
      console.warn(`Number ${num} exceeds safe integer range`)
      return null
    }
  }
  return result
}

The Final Tool#

Based on these ideas, I built: Number Sequence Generator

Features:

  • 9 sequence types
  • Custom start and parameters for arithmetic/geometric
  • One-click copy and download
  • Automatic sum calculation

The implementation isn’t complex, but each sequence type has fascinating mathematical depth worth exploring. Hope this helps.


Related: Percentage Calculator | Number Base Converter