22https://en.wikipedia.org/wiki/Lucas_number
33"""
44
5+ import functools
6+ import math
7+ from typing import Dict
8+
59
610def recursive_lucas_number (n_th_number : int ) -> int :
711 """
@@ -21,14 +25,17 @@ def recursive_lucas_number(n_th_number: int) -> int:
2125 """
2226 if not isinstance (n_th_number , int ):
2327 raise TypeError ("recursive_lucas_number accepts only integer arguments." )
24- if n_th_number == 0 :
25- return 2
26- if n_th_number == 1 :
27- return 1
28-
29- return recursive_lucas_number (n_th_number - 1 ) + recursive_lucas_number (
30- n_th_number - 2
31- )
28+
29+ # Use memoization to cache results and avoid redundant calculations
30+ @functools .lru_cache (maxsize = None )
31+ def _recursive_lucas (n : int ) -> int :
32+ if n == 0 :
33+ return 2
34+ if n == 1 :
35+ return 1
36+ return _recursive_lucas (n - 1 ) + _recursive_lucas (n - 2 )
37+
38+ return _recursive_lucas (n_th_number )
3239
3340
3441def dynamic_lucas_number (n_th_number : int ) -> int :
@@ -49,10 +56,17 @@ def dynamic_lucas_number(n_th_number: int) -> int:
4956 """
5057 if not isinstance (n_th_number , int ):
5158 raise TypeError ("dynamic_lucas_number accepts only integer arguments." )
59+
60+ if n_th_number == 0 :
61+ return 2
62+ if n_th_number == 1 :
63+ return 1
64+
5265 a , b = 2 , 1
53- for _ in range (n_th_number ):
66+ for _ in range (2 , n_th_number + 1 ):
5467 a , b = b , a + b
55- return a
68+
69+ return b
5670
5771
5872def matrix_power_lucas_number (n_th_number : int ) -> int :
@@ -73,46 +87,110 @@ def matrix_power_lucas_number(n_th_number: int) -> int:
7387 """
7488 if not isinstance (n_th_number , int ):
7589 raise TypeError ("matrix_power_lucas_number accepts only integer arguments." )
76-
90+
7791 if n_th_number == 0 :
7892 return 2
7993 if n_th_number == 1 :
8094 return 1
81-
95+
8296 def matrix_mult (a : list [list [int ]], b : list [list [int ]]) -> list [list [int ]]:
8397 return [
84- [
85- a [0 ][0 ] * b [0 ][0 ] + a [0 ][1 ] * b [1 ][0 ],
86- a [0 ][0 ] * b [0 ][1 ] + a [0 ][1 ] * b [1 ][1 ],
87- ],
88- [
89- a [1 ][0 ] * b [0 ][0 ] + a [1 ][1 ] * b [1 ][0 ],
90- a [1 ][0 ] * b [0 ][1 ] + a [1 ][1 ] * b [1 ][1 ],
91- ],
98+ [a [0 ][0 ] * b [0 ][0 ] + a [0 ][1 ] * b [1 ][0 ], a [0 ][0 ] * b [0 ][1 ] + a [0 ][1 ] * b [1 ][1 ]],
99+ [a [1 ][0 ] * b [0 ][0 ] + a [1 ][1 ] * b [1 ][0 ], a [1 ][0 ] * b [0 ][1 ] + a [1 ][1 ] * b [1 ][1 ]],
92100 ]
93-
101+
94102 def matrix_power (matrix : list [list [int ]], power : int ) -> list [list [int ]]:
95103 # Start with identity matrix
96104 result : list [list [int ]] = [[1 , 0 ], [0 , 1 ]]
97105 base = matrix
98-
106+
99107 while power > 0 :
100108 if power % 2 == 1 :
101109 result = matrix_mult (result , base )
102110 base = matrix_mult (base , base )
103111 power //= 2
104-
112+
105113 return result
106-
114+
107115 # Lucas number matrix form: [[1, 1], [1, 0]]
108116 base_matrix = [[1 , 1 ], [1 , 0 ]]
109117 powered_matrix = matrix_power (base_matrix , n_th_number - 1 )
110-
118+
111119 # L(n) = powered_matrix[0][0] * L(1) + powered_matrix[0][1] * L(0)
112120 # Where L(1) = 1, L(0) = 2
113121 return powered_matrix [0 ][0 ] * 1 + powered_matrix [0 ][1 ] * 2
114122
115123
124+ def closed_form_lucas_number (n_th_number : int ) -> int :
125+ """
126+ Returns the nth lucas number using the closed-form formula (Binet's formula)
127+ >>> closed_form_lucas_number(1)
128+ 1
129+ >>> closed_form_lucas_number(20)
130+ 15127
131+ >>> closed_form_lucas_number(0)
132+ 2
133+ >>> closed_form_lucas_number(25)
134+ 167761
135+ >>> closed_form_lucas_number(-1.5)
136+ Traceback (most recent call last):
137+ ...
138+ TypeError: closed_form_lucas_number accepts only integer arguments.
139+ """
140+ if not isinstance (n_th_number , int ):
141+ raise TypeError ("closed_form_lucas_number accepts only integer arguments." )
142+
143+ if n_th_number == 0 :
144+ return 2
145+ if n_th_number == 1 :
146+ return 1
147+
148+ # Golden ratio
149+ phi = (1 + math .sqrt (5 )) / 2
150+ # Conjugate of golden ratio
151+ psi = (1 - math .sqrt (5 )) / 2
152+
153+ # Lucas number closed form: L(n) = phi^n + psi^n
154+ return round (phi ** n_th_number + psi ** n_th_number )
155+
156+
157+ # Global cache for performance optimization
158+ _lucas_cache : Dict [int , int ] = {0 : 2 , 1 : 1 }
159+
160+
161+
162+ def cached_lucas_number (n_th_number : int ) -> int :
163+ """
164+ Returns the nth lucas number using a cached approach for optimal performance
165+ >>> cached_lucas_number(1)
166+ 1
167+ >>> cached_lucas_number(20)
168+ 15127
169+ >>> cached_lucas_number(0)
170+ 2
171+ >>> cached_lucas_number(25)
172+ 167761
173+ >>> cached_lucas_number(-1.5)
174+ Traceback (most recent call last):
175+ ...
176+ TypeError: cached_lucas_number accepts only integer arguments.
177+ """
178+ if not isinstance (n_th_number , int ):
179+ raise TypeError ("cached_lucas_number accepts only integer arguments." )
180+
181+ if n_th_number in _lucas_cache :
182+ return _lucas_cache [n_th_number ]
183+
184+ # Calculate using the fastest method for uncached values
185+ if n_th_number < 70 : # For smaller values, closed form is efficient
186+ result = closed_form_lucas_number (n_th_number )
187+ else : # For larger values, matrix exponentiation is more stable
188+ result = matrix_power_lucas_number (n_th_number )
189+
190+ _lucas_cache [n_th_number ] = result
191+ return result
192+
193+
116194if __name__ == "__main__" :
117195 from doctest import testmod
118196
@@ -124,3 +202,7 @@ def matrix_power(matrix: list[list[int]], power: int) -> list[list[int]]:
124202 print (" " .join (str (dynamic_lucas_number (i )) for i in range (n )))
125203 print ("\n Using matrix exponentiation to calculate lucas series:" )
126204 print (" " .join (str (matrix_power_lucas_number (i )) for i in range (n )))
205+ print ("\n Using closed-form formula to calculate lucas series:" )
206+ print (" " .join (str (closed_form_lucas_number (i )) for i in range (n )))
207+ print ("\n Using cached function to calculate lucas series:" )
208+ print (" " .join (str (cached_lucas_number (i )) for i in range (n )))
0 commit comments