Files
SolutionEuler/solutions/0037.TruncatablePrimes/euler_37_primeclass.py
Sidney Zhang 09ddf3f65c feat(main.py):重构主函数调用方式,将 app() 调用封装到 main() 函数中
 feat(euler_37.py):添加截断质数问题的初始实现
 feat(euler_37_primeclass.py):添加基于质数生成器的截断质数完整解决方案
📝 docs(millerrabin_test.pdf):添加 Miller-Rabin 测试的详细文档
2026-01-07 18:26:00 +08:00

172 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
The number 3797 has an interesting property.
Being prime itself, it is possible to continuously remove digits from left to right,
and remain prime at each stage: 3797, 797, 97, and 7.
Similarly we can work from right to left: 3797, 379, 37, and 3.
Find the sum of the only eleven primes that are both truncatable from left to right and right to left.
NOTE: 2, 3, 5, and 7 are not considered to be truncatable primes.
"""
import math
import random
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} taken: {end_time - start_time:.6f} seconds")
return result
return wrapper
def miller_rabin_test(n: int, k: int = 10) -> bool:
if n < 2:
return False
if n == 2 or n == 3:
return True
if n % 2 == 0 or n % 3 == 0:
return False
r, s = 0, n - 1
while s % 2 == 0:
r += 1
s //= 2
for _ in range(k):
a = random.randrange(2, n - 1)
x = pow(a, s, n)
if x == 1 or x == n - 1:
continue
for _ in range(r - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
class PrimeGenerator:
"""质数生成器"""
_cache = []
def __init__(self):
# self._cache = [] # 缓存已生成的质数
self._generator = self._prime_generator()
@classmethod
def is_prime(cls, n: int) -> bool:
"""检查是否为质数(使用缓存优化)"""
if n < 2:
return False
# 检查缓存
if cls._cache:
limit = int(math.isqrt(n))
for p in cls._cache:
if p > limit:
break
if n % p == 0:
return False
# 使用Miller-Rabin测试
return miller_rabin_test(n)
def _prime_generator(self):
"""生成质数的内部生成器"""
n = 2
yield n
n = 3
while True:
if self.is_prime(n):
PrimeGenerator._cache.append(n)
yield n
n += 2
def __iter__(self):
return self
def __next__(self) -> int:
return next(self._generator)
def __call__(self, start: int = 2):
"""从指定位置开始生成质数"""
# 重置生成器到指定位置
self._generator = self._prime_generator_from(start)
return self
def _prime_generator_from(self, start: int):
"""从指定位置开始的生成器"""
if start < 2:
n = 2
else:
n = start if start % 2 else start + 1
while True:
if self.is_prime(n):
yield n
n += 2
def primes_up_to(self, limit: int):
"""生成所有不超过limit的质数"""
if limit < 2:
return
yield 2
n = 3
while n <= limit:
if self.is_prime(n):
yield n
n += 2
def nth_prime(self, n: int) -> int:
"""获取第n个质数从1开始"""
if n < 1:
raise ValueError("n必须大于0")
# 如果已经在缓存中,直接返回
if n <= len(PrimeGenerator._cache):
return PrimeGenerator._cache[n - 1]
# 否则继续生成直到第n个
gen = self._prime_generator()
for _ in range(n):
result = next(gen)
return result
def truncate_number(number: int) -> list[int]:
"""生成一个数的所有截断形式"""
str_num = str(number)
return [int(str_num[:i]) for i in range(1, len(str_num))] + [
int(str_num[i:]) for i in range(len(str_num))
]
@timer
def main() -> None:
primes = PrimeGenerator()
res = []
n = 0
while n < 11:
prime = next(primes)
if prime < 10:
continue
truncate = truncate_number(prime)
if all(primes.is_prime(trunc) for trunc in truncate):
res.append(prime)
print(prime)
n += 1
print(f"Sum is : {sum(res)}")
if __name__ == "__main__":
main()