Facebook Pixel

2648. Generate Fibonacci Sequence

Problem Description

This problem asks you to implement a generator function that produces the Fibonacci sequence infinitely.

The Fibonacci sequence follows the mathematical relation: Xₙ = Xₙ₋₁ + Xₙ₋₂, where each number is the sum of the two preceding numbers.

The sequence starts with 0 and 1, and continues as: 0, 1, 1, 2, 3, 5, 8, 13, ...

Your task is to create a generator function fibGenerator() that:

  • Returns a generator object
  • When calling next() on the generator, it yields the next number in the Fibonacci sequence
  • The generator should be able to produce Fibonacci numbers indefinitely

For example:

  • First call to gen.next().value returns 0
  • Second call to gen.next().value returns 1
  • Third call to gen.next().value returns 1
  • Fourth call to gen.next().value returns 2
  • And so on...

The solution uses two variables a and b to track the current and next Fibonacci numbers. Initially, a = 0 and b = 1. In each iteration:

  1. Yield the current value a
  2. Update the values using array destructuring: [a, b] = [b, a + b], which simultaneously sets a to the old value of b and b to the sum of old a and b

This approach maintains constant space complexity while generating the sequence infinitely.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing that we only need to keep track of the last two numbers in the sequence to generate the next one. Since the Fibonacci formula Xₙ = Xₙ₋₁ + Xₙ₋₂ only depends on the two most recent values, we don't need to store the entire sequence.

Think of it like climbing stairs where each step depends on where your two feet were previously positioned. You don't need to remember every step you've taken - just where your feet are now.

We start with the base cases: the first two Fibonacci numbers are 0 and 1. These serve as our initial "two feet" positions.

To generate each subsequent number:

  • The current number to output is always the smaller of our two stored values
  • The next number in our pair is always the sum of our current two values
  • We slide our "window" forward by replacing the smaller value with the larger one, and the larger value with their sum

The generator function is perfect for this problem because:

  1. We need to produce values on-demand rather than all at once
  2. The sequence is potentially infinite
  3. Each value depends only on the previous state, making it ideal for maintaining state between yield statements

The elegant array destructuring [a, b] = [b, a + b] performs the "sliding window" update in one line. This simultaneously moves b into a's position and calculates the new b as a + b, using the original values before any assignment happens.

Solution Approach

The implementation uses a generator function with JavaScript's function* syntax to create an infinite sequence producer.

State Management: We maintain two variables:

  • a: Stores the current Fibonacci number to be yielded
  • b: Stores the next Fibonacci number in the sequence

Initially, we set a = 0 and b = 1, representing the first two numbers of the Fibonacci sequence.

The Infinite Loop: The while (true) loop ensures our generator can produce values indefinitely. This is safe because generators are lazy - they only compute values when explicitly requested via next().

Core Algorithm Steps:

  1. Yield Current Value: yield a pauses execution and returns the current Fibonacci number
  2. State Update: [a, b] = [b, a + b] performs the critical update:
    • The new a becomes the old b
    • The new b becomes the sum of old a and old b
    • This happens atomically using destructuring assignment

Example Walkthrough:

Initial: a = 0, b = 1
Call 1: yield 0, then a = 1, b = 0 + 1 = 1
Call 2: yield 1, then a = 1, b = 1 + 1 = 2
Call 3: yield 1, then a = 2, b = 1 + 2 = 3
Call 4: yield 2, then a = 3, b = 2 + 3 = 5
Call 5: yield 3, then a = 5, b = 3 + 5 = 8

TypeScript Type Annotations: The function signature Generator<number, any, number> indicates:

  • number: The type of values yielded
  • any: The return type (not used since it runs infinitely)
  • number: The type of values that can be passed to next() (not used in this implementation)

This approach achieves O(1) space complexity and O(1) time complexity per generated number, making it highly efficient for on-demand Fibonacci number generation.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's trace through the generator function to see how it produces the first 5 Fibonacci numbers:

Initial Setup:

const gen = fibGenerator();
// Internal state: a = 0, b = 1

First call - gen.next():

  • Current state: a = 0, b = 1
  • Yield a → returns 0
  • Update: [a, b] = [b, a + b][a, b] = [1, 0 + 1]a = 1, b = 1

Second call - gen.next():

  • Current state: a = 1, b = 1
  • Yield a → returns 1
  • Update: [a, b] = [b, a + b][a, b] = [1, 1 + 1]a = 1, b = 2

Third call - gen.next():

  • Current state: a = 1, b = 2
  • Yield a → returns 1
  • Update: [a, b] = [b, a + b][a, b] = [2, 1 + 2]a = 2, b = 3

Fourth call - gen.next():

  • Current state: a = 2, b = 3
  • Yield a → returns 2
  • Update: [a, b] = [b, a + b][a, b] = [3, 2 + 3]a = 3, b = 5

Fifth call - gen.next():

  • Current state: a = 3, b = 5
  • Yield a → returns 3
  • Update: [a, b] = [b, a + b][a, b] = [5, 3 + 5]a = 5, b = 8

Sequence generated: 0, 1, 1, 2, 3, ...

Notice how we only ever store two numbers at a time. The variable a always holds the current Fibonacci number to output, while b holds the next one in line. The destructuring assignment elegantly slides our two-number window forward through the infinite sequence.

Solution Implementation

1def fibGenerator():
2    """
3    Generator function that produces Fibonacci sequence numbers infinitely.
4    The Fibonacci sequence starts with 0 and 1, and each subsequent number
5    is the sum of the two preceding numbers.
6  
7    Yields:
8        int: The next Fibonacci number in the sequence
9    """
10    # Initialize the first two Fibonacci numbers
11    current = 0  # First Fibonacci number (F0)
12    next_num = 1  # Second Fibonacci number (F1)
13  
14    # Infinite loop to generate Fibonacci numbers
15    while True:
16        # Yield the current Fibonacci number
17        yield current
18      
19        # Calculate the next Fibonacci number using tuple unpacking
20        # Move 'next_num' to 'current' and calculate new 'next_num' as sum of both
21        current, next_num = next_num, current + next_num
22
23
24# Usage example:
25# gen = fibGenerator()
26# next(gen)  # 0
27# next(gen)  # 1
28# next(gen)  # 1
29# next(gen)  # 2
30# next(gen)  # 3
31# next(gen)  # 5
32# next(gen)  # 8
33# ...and so on
34
1/**
2 * Class that provides a Fibonacci sequence generator using Iterator pattern.
3 * The Fibonacci sequence starts with 0 and 1, and each subsequent number
4 * is the sum of the two preceding numbers.
5 */
6import java.util.Iterator;
7
8public class FibGenerator implements Iterator<Integer> {
9    // Instance variables to maintain state between iterations
10    private int current;  // Current Fibonacci number (F_n)
11    private int next;     // Next Fibonacci number (F_n+1)
12  
13    /**
14     * Constructor initializes the first two Fibonacci numbers.
15     */
16    public FibGenerator() {
17        this.current = 0;  // First Fibonacci number (F0)
18        this.next = 1;     // Second Fibonacci number (F1)
19    }
20  
21    /**
22     * Checks if there is a next element in the sequence.
23     * Since Fibonacci sequence is infinite, always returns true.
24     * 
25     * @return true (always, as sequence is infinite)
26     */
27    @Override
28    public boolean hasNext() {
29        return true;  // Infinite sequence
30    }
31  
32    /**
33     * Returns the next Fibonacci number in the sequence and advances the generator.
34     * 
35     * @return the next Fibonacci number
36     */
37    @Override
38    public Integer next() {
39        // Store the current value to return
40        int valueToReturn = current;
41      
42        // Calculate the next Fibonacci number
43        // Move 'next' to 'current' and calculate new 'next' as sum of both
44        int temp = current + next;
45        current = next;
46        next = temp;
47      
48        // Return the stored value
49        return valueToReturn;
50    }
51  
52    /**
53     * Factory method to create a new FibGenerator instance.
54     * 
55     * @return a new FibGenerator iterator
56     */
57    public static FibGenerator fibGenerator() {
58        return new FibGenerator();
59    }
60  
61    /**
62     * Usage example:
63     * FibGenerator gen = FibGenerator.fibGenerator();
64     * gen.next(); // 0
65     * gen.next(); // 1
66     * gen.next(); // 1
67     * gen.next(); // 2
68     * gen.next(); // 3
69     * gen.next(); // 5
70     * gen.next(); // 8
71     * ...and so on
72     */
73}
74
1#include <iostream>
2#include <coroutine>
3#include <optional>
4
5/**
6 * Generator template for creating coroutines that yield values
7 */
8template<typename T>
9struct Generator {
10    struct promise_type {
11        T current_value;
12      
13        std::suspend_always yield_value(T value) {
14            current_value = value;
15            return {};
16        }
17      
18        Generator get_return_object() {
19            return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
20        }
21      
22        std::suspend_always initial_suspend() { return {}; }
23        std::suspend_always final_suspend() noexcept { return {}; }
24        void unhandled_exception() { std::terminate(); }
25        void return_void() {}
26    };
27  
28    std::coroutine_handle<promise_type> handle;
29  
30    explicit Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
31  
32    ~Generator() {
33        if (handle) {
34            handle.destroy();
35        }
36    }
37  
38    // Move constructor and assignment
39    Generator(Generator&& other) noexcept : handle(std::exchange(other.handle, {})) {}
40    Generator& operator=(Generator&& other) noexcept {
41        if (this != &other) {
42            if (handle) {
43                handle.destroy();
44            }
45            handle = std::exchange(other.handle, {});
46        }
47        return *this;
48    }
49  
50    // Delete copy operations
51    Generator(const Generator&) = delete;
52    Generator& operator=(const Generator&) = delete;
53  
54    /**
55     * Get the next value from the generator
56     * @return Optional containing the value if available
57     */
58    std::optional<T> next() {
59        if (handle && !handle.done()) {
60            handle.resume();
61            if (!handle.done()) {
62                return handle.promise().current_value;
63            }
64        }
65        return std::nullopt;
66    }
67};
68
69/**
70 * Generator function that produces Fibonacci sequence numbers infinitely.
71 * The Fibonacci sequence starts with 0 and 1, and each subsequent number
72 * is the sum of the two preceding numbers.
73 * 
74 * @return A generator that yields Fibonacci numbers
75 */
76Generator<int> fibGenerator() {
77    // Initialize the first two Fibonacci numbers
78    int current = 0;  // First Fibonacci number (F0)
79    int next = 1;     // Second Fibonacci number (F1)
80  
81    // Infinite loop to generate Fibonacci numbers
82    while (true) {
83        // Yield the current Fibonacci number
84        co_yield current;
85      
86        // Calculate the next Fibonacci number
87        // Move 'next' to 'current' and calculate new 'next' as sum of both
88        int temp = current + next;
89        current = next;
90        next = temp;
91    }
92}
93
94/**
95 * Usage example:
96 * auto gen = fibGenerator();
97 * gen.next().value(); // 0
98 * gen.next().value(); // 1
99 * gen.next().value(); // 1
100 * gen.next().value(); // 2
101 * gen.next().value(); // 3
102 * gen.next().value(); // 5
103 * gen.next().value(); // 8
104 * ...and so on
105 */
106
1/**
2 * Generator function that produces Fibonacci sequence numbers infinitely.
3 * The Fibonacci sequence starts with 0 and 1, and each subsequent number
4 * is the sum of the two preceding numbers.
5 * 
6 * @returns {Generator<number, any, number>} A generator that yields Fibonacci numbers
7 */
8function* fibGenerator(): Generator<number, any, number> {
9    // Initialize the first two Fibonacci numbers
10    let current: number = 0;  // First Fibonacci number (F0)
11    let next: number = 1;     // Second Fibonacci number (F1)
12  
13    // Infinite loop to generate Fibonacci numbers
14    while (true) {
15        // Yield the current Fibonacci number
16        yield current;
17      
18        // Calculate the next Fibonacci number using destructuring assignment
19        // Move 'next' to 'current' and calculate new 'next' as sum of both
20        [current, next] = [next, current + next];
21    }
22}
23
24/**
25 * Usage example:
26 * const gen = fibGenerator();
27 * gen.next().value; // 0
28 * gen.next().value; // 1
29 * gen.next().value; // 1
30 * gen.next().value; // 2
31 * gen.next().value; // 3
32 * gen.next().value; // 5
33 * gen.next().value; // 8
34 * ...and so on
35 */
36

Time and Space Complexity

Time Complexity: O(1) per next() call

Each call to next() performs a constant amount of work:

  • One yield operation
  • One array destructuring assignment [a, b] = [b, a + b]
  • Two additions and two assignments

Since these operations don't depend on the size of any input or the number of previous calls, each iteration has constant time complexity.

Space Complexity: O(1)

The generator maintains only two variables a and b to track the current and next Fibonacci numbers, regardless of how many times next() is called. The space usage remains constant throughout the generator's lifetime. The temporary array [b, a + b] created during destructuring is also constant size and doesn't grow with the number of iterations.

Common Pitfalls

1. Integer Overflow for Large Fibonacci Numbers

The most critical pitfall is that Fibonacci numbers grow exponentially. After generating around 90-100 numbers, the values can exceed the maximum safe integer limit in many programming languages. In JavaScript, this limit is Number.MAX_SAFE_INTEGER (2^53 - 1), and in Python, while integers can grow arbitrarily large, this can lead to performance degradation and memory issues.

Solution:

  • In JavaScript/TypeScript, use BigInt for large Fibonacci numbers:
function* fibGenerator() {
    let a = 0n, b = 1n;
    while (true) {
        yield a;
        [a, b] = [b, a + b];
    }
}
  • In Python, be aware of memory consumption for very large numbers and consider implementing a maximum limit or using modular arithmetic if only remainders are needed:
def fibGenerator(max_value=None):
    current, next_num = 0, 1
    while True:
        if max_value and current > max_value:
            return  # Stop generation when limit is reached
        yield current
        current, next_num = next_num, current + next_num

2. Forgetting to Store the Generator Object

A common mistake is calling the generator function multiple times instead of storing the generator object:

# Wrong - creates new generator each time
fibGenerator().__next__()  # 0
fibGenerator().__next__()  # 0 (always returns first value)

# Correct - store and reuse the generator
gen = fibGenerator()
next(gen)  # 0
next(gen)  # 1

3. Attempting to Iterate Backwards or Reset

Generators are forward-only iterators. Once a value is consumed, you cannot go back or reset without creating a new generator instance. Developers might expect to reset or rewind the sequence.

Solution: If you need to access previous values or reset, consider:

  • Creating a new generator instance
  • Implementing a class-based approach with reset functionality:
class FibonacciGenerator:
    def __init__(self):
        self.reset()
  
    def reset(self):
        self.current = 0
        self.next_num = 1
  
    def get_next(self):
        value = self.current
        self.current, self.next_num = self.next_num, self.current + self.next_num
        return value

4. Memory Issues When Collecting All Values

Trying to convert an infinite generator to a list will cause memory exhaustion:

# DON'T DO THIS - will run forever and crash
all_fibs = list(fibGenerator())

# Instead, use itertools.islice for a specific range
from itertools import islice
first_10_fibs = list(islice(fibGenerator(), 10))
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the maximum element can be found in:


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More