Skip to content

Commit 87630a2

Browse files
authored
change lucas_series.py
1 parent 2e27079 commit 87630a2

File tree

1 file changed

+107
-25
lines changed

1 file changed

+107
-25
lines changed

maths/lucas_series.py

Lines changed: 107 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
https://en.wikipedia.org/wiki/Lucas_number
33
"""
44

5+
import functools
6+
import math
7+
from typing import Dict
8+
59

610
def 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

3441
def 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

5872
def 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+
116194
if __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("\nUsing matrix exponentiation to calculate lucas series:")
126204
print(" ".join(str(matrix_power_lucas_number(i)) for i in range(n)))
205+
print("\nUsing closed-form formula to calculate lucas series:")
206+
print(" ".join(str(closed_form_lucas_number(i)) for i in range(n)))
207+
print("\nUsing cached function to calculate lucas series:")
208+
print(" ".join(str(cached_lucas_number(i)) for i in range(n)))

0 commit comments

Comments
 (0)