Skip to content

Commit 64ec09e

Browse files
authored
Merge pull request #1 from codingU4EA/copilot/fix-fb581a28-f4a0-487f-bc83-f5f672097ce4
Add comprehensive Fibonacci sequence Python implementation guide. See comments from copilot.
2 parents 1685434 + 6ee4696 commit 64ec09e

File tree

1 file changed

+351
-0
lines changed

1 file changed

+351
-0
lines changed

fibonacci-python-guide.md

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
# How to Write a Fibonacci Sequence in Python
2+
3+
## What is the Fibonacci Sequence?
4+
5+
The Fibonacci sequence is a famous mathematical sequence where each number is the sum of the two preceding ones. The sequence typically starts with 0 and 1:
6+
7+
```
8+
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
9+
```
10+
11+
Mathematically, it can be defined as:
12+
- F(0) = 0
13+
- F(1) = 1
14+
- F(n) = F(n-1) + F(n-2) for n > 1
15+
16+
## Implementation Methods
17+
18+
There are several ways to implement the Fibonacci sequence in Python, each with different trade-offs in terms of performance, memory usage, and code readability.
19+
20+
### 1. Recursive Implementation
21+
22+
The most straightforward approach that directly follows the mathematical definition:
23+
24+
```python
25+
def fibonacci_recursive(n):
26+
"""
27+
Calculate the nth Fibonacci number using recursion.
28+
29+
Args:
30+
n (int): The position in the Fibonacci sequence (0-indexed)
31+
32+
Returns:
33+
int: The nth Fibonacci number
34+
35+
Time Complexity: O(2^n) - Exponential
36+
Space Complexity: O(n) - Due to recursion stack
37+
"""
38+
if n < 0:
39+
raise ValueError("n must be non-negative")
40+
if n <= 1:
41+
return n
42+
return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)
43+
44+
# Example usage
45+
print(fibonacci_recursive(10)) # Output: 55
46+
```
47+
48+
**Pros:**
49+
- Simple and intuitive
50+
- Directly follows the mathematical definition
51+
52+
**Cons:**
53+
- Very inefficient for large numbers due to repeated calculations
54+
- Exponential time complexity
55+
- Can cause stack overflow for large inputs
56+
57+
### 2. Iterative Implementation
58+
59+
A much more efficient approach using a loop:
60+
61+
```python
62+
def fibonacci_iterative(n):
63+
"""
64+
Calculate the nth Fibonacci number using iteration.
65+
66+
Args:
67+
n (int): The position in the Fibonacci sequence (0-indexed)
68+
69+
Returns:
70+
int: The nth Fibonacci number
71+
72+
Time Complexity: O(n) - Linear
73+
Space Complexity: O(1) - Constant
74+
"""
75+
if n < 0:
76+
raise ValueError("n must be non-negative")
77+
if n <= 1:
78+
return n
79+
80+
a, b = 0, 1
81+
for _ in range(2, n + 1):
82+
a, b = b, a + b
83+
return b
84+
85+
# Example usage
86+
print(fibonacci_iterative(10)) # Output: 55
87+
```
88+
89+
**Pros:**
90+
- Much more efficient than recursive approach
91+
- Linear time complexity
92+
- Constant space complexity
93+
94+
**Cons:**
95+
- Slightly more complex logic than recursive approach
96+
97+
### 3. Memoized Recursive Implementation
98+
99+
Combines the simplicity of recursion with efficiency through caching:
100+
101+
```python
102+
def fibonacci_memoized(n, memo={}):
103+
"""
104+
Calculate the nth Fibonacci number using memoized recursion.
105+
106+
Args:
107+
n (int): The position in the Fibonacci sequence (0-indexed)
108+
memo (dict): Cache for previously calculated values
109+
110+
Returns:
111+
int: The nth Fibonacci number
112+
113+
Time Complexity: O(n) - Linear
114+
Space Complexity: O(n) - Due to memoization and recursion stack
115+
"""
116+
if n < 0:
117+
raise ValueError("n must be non-negative")
118+
if n in memo:
119+
return memo[n]
120+
if n <= 1:
121+
return n
122+
123+
memo[n] = fibonacci_memoized(n - 1, memo) + fibonacci_memoized(n - 2, memo)
124+
return memo[n]
125+
126+
# Alternative using functools.lru_cache decorator
127+
from functools import lru_cache
128+
129+
@lru_cache(maxsize=None)
130+
def fibonacci_cached(n):
131+
"""
132+
Calculate the nth Fibonacci number using cached recursion.
133+
134+
Args:
135+
n (int): The position in the Fibonacci sequence (0-indexed)
136+
137+
Returns:
138+
int: The nth Fibonacci number
139+
"""
140+
if n < 0:
141+
raise ValueError("n must be non-negative")
142+
if n <= 1:
143+
return n
144+
return fibonacci_cached(n - 1) + fibonacci_cached(n - 2)
145+
146+
# Example usage
147+
print(fibonacci_memoized(10)) # Output: 55
148+
print(fibonacci_cached(10)) # Output: 55
149+
```
150+
151+
**Pros:**
152+
- Combines simplicity of recursion with efficiency
153+
- Linear time complexity after initial calculations
154+
155+
**Cons:**
156+
- Uses more memory to store cached values
157+
- Still has recursion stack limitations for very large numbers
158+
159+
### 4. Generator Implementation
160+
161+
Perfect for generating sequences or when you don't need all values at once:
162+
163+
```python
164+
def fibonacci_generator():
165+
"""
166+
Generate Fibonacci numbers indefinitely.
167+
168+
Yields:
169+
int: Next Fibonacci number in sequence
170+
171+
Space Complexity: O(1) - Constant
172+
"""
173+
a, b = 0, 1
174+
while True:
175+
yield a
176+
a, b = b, a + b
177+
178+
def fibonacci_sequence(count):
179+
"""
180+
Generate the first 'count' Fibonacci numbers.
181+
182+
Args:
183+
count (int): Number of Fibonacci numbers to generate
184+
185+
Returns:
186+
list: List of the first 'count' Fibonacci numbers
187+
"""
188+
if count < 0:
189+
raise ValueError("count must be non-negative")
190+
191+
fib_gen = fibonacci_generator()
192+
return [next(fib_gen) for _ in range(count)]
193+
194+
# Example usage
195+
fib_gen = fibonacci_generator()
196+
first_10 = [next(fib_gen) for _ in range(10)]
197+
print(first_10) # Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
198+
199+
print(fibonacci_sequence(10)) # Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
200+
```
201+
202+
**Pros:**
203+
- Memory efficient for large sequences
204+
- Can generate infinite sequences
205+
- Lazy evaluation
206+
207+
**Cons:**
208+
- More complex for simple use cases
209+
210+
### 5. Matrix Exponentiation (Advanced)
211+
212+
For very large Fibonacci numbers, matrix exponentiation provides O(log n) complexity:
213+
214+
```python
215+
def matrix_multiply(A, B):
216+
"""Multiply two 2x2 matrices."""
217+
return [
218+
[A[0][0] * B[0][0] + A[0][1] * B[1][0], A[0][0] * B[0][1] + A[0][1] * B[1][1]],
219+
[A[1][0] * B[0][0] + A[1][1] * B[1][0], A[1][0] * B[0][1] + A[1][1] * B[1][1]]
220+
]
221+
222+
def matrix_power(matrix, n):
223+
"""Calculate matrix to the power of n using fast exponentiation."""
224+
if n == 1:
225+
return matrix
226+
if n % 2 == 0:
227+
half_power = matrix_power(matrix, n // 2)
228+
return matrix_multiply(half_power, half_power)
229+
else:
230+
return matrix_multiply(matrix, matrix_power(matrix, n - 1))
231+
232+
def fibonacci_matrix(n):
233+
"""
234+
Calculate the nth Fibonacci number using matrix exponentiation.
235+
236+
Args:
237+
n (int): The position in the Fibonacci sequence (0-indexed)
238+
239+
Returns:
240+
int: The nth Fibonacci number
241+
242+
Time Complexity: O(log n) - Logarithmic
243+
Space Complexity: O(log n) - Due to recursion in matrix_power
244+
"""
245+
if n < 0:
246+
raise ValueError("n must be non-negative")
247+
if n <= 1:
248+
return n
249+
250+
# Base matrix [[1, 1], [1, 0]]
251+
base_matrix = [[1, 1], [1, 0]]
252+
result_matrix = matrix_power(base_matrix, n)
253+
return result_matrix[0][1]
254+
255+
# Example usage
256+
print(fibonacci_matrix(10)) # Output: 55
257+
```
258+
259+
**Pros:**
260+
- Most efficient for very large numbers
261+
- Logarithmic time complexity
262+
263+
**Cons:**
264+
- Complex implementation
265+
- Overkill for small to medium numbers
266+
267+
## Performance Comparison
268+
269+
Here's a simple script to compare the performance of different implementations:
270+
271+
```python
272+
import time
273+
274+
def benchmark_fibonacci(func, n, name):
275+
"""Benchmark a Fibonacci function."""
276+
start_time = time.time()
277+
try:
278+
result = func(n)
279+
end_time = time.time()
280+
print(f"{name}: F({n}) = {result}, Time: {end_time - start_time:.6f} seconds")
281+
except RecursionError:
282+
print(f"{name}: RecursionError for F({n})")
283+
except Exception as e:
284+
print(f"{name}: Error - {e}")
285+
286+
# Test with n = 35
287+
n = 35
288+
print(f"Calculating F({n}):")
289+
benchmark_fibonacci(fibonacci_iterative, n, "Iterative")
290+
benchmark_fibonacci(fibonacci_memoized, n, "Memoized")
291+
benchmark_fibonacci(fibonacci_matrix, n, "Matrix")
292+
# Note: Recursive will be very slow for n=35, uncomment if you want to test
293+
# benchmark_fibonacci(fibonacci_recursive, n, "Recursive")
294+
```
295+
296+
## Best Practices and Recommendations
297+
298+
1. **For most use cases**: Use the **iterative implementation** - it's simple, efficient, and easy to understand.
299+
300+
2. **For generating sequences**: Use the **generator implementation** - it's memory efficient and allows for lazy evaluation.
301+
302+
3. **For very large numbers**: Consider the **matrix exponentiation** approach for optimal performance.
303+
304+
4. **For learning purposes**: Start with the **recursive implementation** to understand the concept, then move to more efficient versions.
305+
306+
5. **Error handling**: Always validate input parameters and handle edge cases appropriately.
307+
308+
6. **Documentation**: Include clear docstrings explaining the function's purpose, parameters, return values, and complexity.
309+
310+
## Common Use Cases
311+
312+
### Checking if a number is in the Fibonacci sequence
313+
314+
```python
315+
def is_fibonacci(num):
316+
"""Check if a number is in the Fibonacci sequence."""
317+
if num < 0:
318+
return False
319+
320+
# Generate Fibonacci numbers until we reach or exceed the target
321+
a, b = 0, 1
322+
while a < num:
323+
a, b = b, a + b
324+
325+
return a == num
326+
327+
# Example usage
328+
print(is_fibonacci(21)) # True
329+
print(is_fibonacci(20)) # False
330+
```
331+
332+
### Finding the nth Fibonacci number modulo m
333+
334+
```python
335+
def fibonacci_mod(n, m):
336+
"""Calculate the nth Fibonacci number modulo m."""
337+
if n <= 1:
338+
return n % m
339+
340+
a, b = 0, 1
341+
for _ in range(2, n + 1):
342+
a, b = b, (a + b) % m
343+
return b
344+
345+
# Example usage
346+
print(fibonacci_mod(100, 1000)) # Last 3 digits of F(100)
347+
```
348+
349+
## Conclusion
350+
351+
The Fibonacci sequence offers an excellent example for learning different programming techniques in Python. Start with the simple recursive approach to understand the concept, then progress to more efficient implementations based on your specific needs. The iterative approach is usually the best balance of simplicity and efficiency for most practical applications.

0 commit comments

Comments
 (0)