1
1
"""
2
- This is a pure Python implementation of Dynamic Programming solution to the fibonacci
2
+ This is a pure Python implementation of Dynamic Programming solution to the Fibonacci
3
3
sequence problem.
4
+
5
+ The Fibonacci sequence is a series of numbers where each number is the sum of the
6
+ two preceding ones, usually starting with 0 and 1. The sequence begins:
7
+ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
8
+
9
+ Reference: https://en.wikipedia.org/wiki/Fibonacci_number
10
+
11
+ Time Complexity: O(n) for first calculation, O(1) for subsequent calls with memoization
12
+ Space Complexity: O(n) for storing the sequence
4
13
"""
5
14
6
15
7
16
class Fibonacci :
17
+ """
18
+ Dynamic Programming implementation of Fibonacci sequence generator.
19
+
20
+ This class maintains a memoized sequence of Fibonacci numbers and can efficiently
21
+ generate new numbers by building on previously calculated values.
22
+
23
+ Attributes:
24
+ sequence (list[int]): Memoized Fibonacci sequence starting with [0, 1]
25
+ """
26
+
8
27
def __init__ (self ) -> None :
28
+ """Initialize the Fibonacci sequence with the first two numbers."""
9
29
self .sequence = [0 , 1 ]
10
-
11
- def get (self , index : int ) -> list :
30
+
31
+ def get (self , index : int ) -> list [ int ] :
12
32
"""
13
- Get the Fibonacci number of `index`. If the number does not exist,
14
- calculate all missing numbers leading up to the number of `index`.
15
-
16
- >>> Fibonacci().get(10)
17
- [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
18
- >>> Fibonacci().get(5)
19
- [0, 1, 1, 2, 3]
33
+ Get the first `index` Fibonacci numbers. If numbers don't exist in the sequence,
34
+ calculate all missing numbers leading up to the required index.
35
+
36
+ Args:
37
+ index (int): Number of Fibonacci numbers to return (must be non-negative)
38
+
39
+ Returns:
40
+ list[int]: List containing the first `index` Fibonacci numbers
41
+
42
+ Raises:
43
+ ValueError: If index is negative
44
+
45
+ Examples:
46
+ >>> fib = Fibonacci()
47
+ >>> fib.get(0)
48
+ []
49
+ >>> fib.get(1)
50
+ [0]
51
+ >>> fib.get(2)
52
+ [0, 1]
53
+ >>> fib.get(5)
54
+ [0, 1, 1, 2, 3]
55
+ >>> fib.get(10)
56
+ [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
57
+ >>> fib.get(1) # Test memoization - should not recalculate
58
+ [0]
59
+ >>> fib.get(15) # Test extending existing sequence
60
+ [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
61
+
62
+ # Test edge cases
63
+ >>> fib_new = Fibonacci()
64
+ >>> fib_new.get(0)
65
+ []
66
+ >>> fib_new.get(1)
67
+ [0]
68
+ >>> fib_new.get(2)
69
+ [0, 1]
70
+
71
+ # Test error handling
72
+ >>> fib_error = Fibonacci()
73
+ >>> fib_error.get(-1)
74
+ Traceback (most recent call last):
75
+ ...
76
+ ValueError: Index must be non-negative, got -1
77
+ >>> fib_error.get(-5)
78
+ Traceback (most recent call last):
79
+ ...
80
+ ValueError: Index must be non-negative, got -5
81
+
82
+ # Test large numbers
83
+ >>> fib_large = Fibonacci()
84
+ >>> result = fib_large.get(20)
85
+ >>> len(result)
86
+ 20
87
+ >>> result[-1] # 20th Fibonacci number (0-indexed, so 19th position)
88
+ 4181
89
+ >>> result[0], result[1], result[2]
90
+ (0, 1, 1)
91
+
92
+ # Test sequence correctness
93
+ >>> fib_test = Fibonacci()
94
+ >>> seq = fib_test.get(8)
95
+ >>> all(seq[i] == seq[i-1] + seq[i-2] for i in range(2, len(seq)))
96
+ True
20
97
"""
21
- if (difference := index - (len (self .sequence ) - 2 )) >= 1 :
98
+ if index < 0 :
99
+ raise ValueError (f"Index must be non-negative, got { index } " )
100
+
101
+ if index == 0 :
102
+ return []
103
+
104
+ # Calculate missing numbers if needed
105
+ if (difference := index - len (self .sequence )) > 0 :
22
106
for _ in range (difference ):
23
107
self .sequence .append (self .sequence [- 1 ] + self .sequence [- 2 ])
108
+
24
109
return self .sequence [:index ]
110
+
111
+ def get_nth (self , n : int ) -> int :
112
+ """
113
+ Get the nth Fibonacci number (0-indexed).
114
+
115
+ Args:
116
+ n (int): The index of the Fibonacci number to retrieve
117
+
118
+ Returns:
119
+ int: The nth Fibonacci number
120
+
121
+ Raises:
122
+ ValueError: If n is negative
123
+
124
+ Examples:
125
+ >>> fib = Fibonacci()
126
+ >>> fib.get_nth(0)
127
+ 0
128
+ >>> fib.get_nth(1)
129
+ 1
130
+ >>> fib.get_nth(2)
131
+ 1
132
+ >>> fib.get_nth(5)
133
+ 5
134
+ >>> fib.get_nth(10)
135
+ 55
136
+ >>> fib.get_nth(-1)
137
+ Traceback (most recent call last):
138
+ ...
139
+ ValueError: Index must be non-negative, got -1
140
+ """
141
+ if n < 0 :
142
+ raise ValueError (f"Index must be non-negative, got { n } " )
143
+
144
+ # Extend sequence if needed
145
+ if n >= len (self .sequence ):
146
+ difference = n - len (self .sequence ) + 1
147
+ for _ in range (difference ):
148
+ self .sequence .append (self .sequence [- 1 ] + self .sequence [- 2 ])
149
+
150
+ return self .sequence [n ]
151
+
152
+ def reset (self ) -> None :
153
+ """
154
+ Reset the sequence to its initial state.
155
+
156
+ Examples:
157
+ >>> fib = Fibonacci()
158
+ >>> fib.get(10)
159
+ [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
160
+ >>> len(fib.sequence)
161
+ 10
162
+ >>> fib.reset()
163
+ >>> fib.sequence
164
+ [0, 1]
165
+ """
166
+ self .sequence = [0 , 1 ]
25
167
26
168
27
169
def main () -> None :
170
+ """
171
+ Interactive CLI for generating Fibonacci numbers.
172
+
173
+ Allows users to input indices and get the corresponding Fibonacci sequence.
174
+ Supports commands: 'exit', 'quit', 'reset', 'help'
175
+ """
28
176
print (
29
- "Fibonacci Series Using Dynamic Programming\n " ,
30
- "Enter the index of the Fibonacci number you want to calculate " ,
31
- "in the prompt below. (To exit enter exit or Ctrl-C)\n " ,
32
- sep = "" ,
177
+ "Fibonacci Series Using Dynamic Programming\n "
178
+ "Enter the index of the Fibonacci number you want to calculate\n "
179
+ "Commands: 'exit'/'quit' to exit, 'reset' to clear cache, 'help' for help\n "
33
180
)
181
+
34
182
fibonacci = Fibonacci ()
35
-
183
+
36
184
while True :
37
- prompt : str = input (">> " )
38
- if prompt in {"exit" , "quit" }:
39
- break
40
-
41
185
try :
42
- index : int = int (prompt )
43
- except ValueError :
44
- print ("Enter a number or 'exit'" )
45
- continue
46
-
47
- print (fibonacci .get (index ))
186
+ prompt : str = input (">> " ).strip ().lower ()
187
+
188
+ if prompt in {"exit" , "quit" }:
189
+ print ("Goodbye!" )
190
+ break
191
+ elif prompt == "reset" :
192
+ fibonacci .reset ()
193
+ print ("Fibonacci sequence cache cleared." )
194
+ continue
195
+ elif prompt == "help" :
196
+ print (
197
+ "Commands:\n "
198
+ " <number> - Get first N Fibonacci numbers\n "
199
+ " reset - Clear the memoized sequence\n "
200
+ " help - Show this help message\n "
201
+ " exit/quit - Exit the program\n "
202
+ )
203
+ continue
204
+ elif prompt == "" :
205
+ continue
206
+
207
+ try :
208
+ index : int = int (prompt )
209
+ if index < 0 :
210
+ print ("Please enter a non-negative number." )
211
+ continue
212
+
213
+ result = fibonacci .get (index )
214
+ if not result :
215
+ print ("[]" )
216
+ else :
217
+ print (f"First { index } Fibonacci numbers: { result } " )
218
+ if index > 0 :
219
+ print (f"The { index } th Fibonacci number is: { result [- 1 ]} " )
220
+
221
+ except ValueError :
222
+ print ("Invalid input. Enter a number or use 'help' for commands." )
223
+
224
+ except KeyboardInterrupt :
225
+ print ("\n Goodbye!" )
226
+ break
227
+ except EOFError :
228
+ print ("\n Goodbye!" )
229
+ break
48
230
49
231
50
232
if __name__ == "__main__" :
51
- main ()
233
+ main ()
0 commit comments