✨ feat(main.py):重构主函数调用方式,将 app() 调用封装到 main() 函数中
✨ feat(euler_37.py):添加截断质数问题的初始实现 ✨ feat(euler_37_primeclass.py):添加基于质数生成器的截断质数完整解决方案 📝 docs(millerrabin_test.pdf):添加 Miller-Rabin 测试的详细文档
This commit is contained in:
6
main.py
6
main.py
@@ -64,5 +64,9 @@ def version():
|
|||||||
typer.echo("projecteuler solution version 0.1.0")
|
typer.echo("projecteuler solution version 0.1.0")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def main():
|
||||||
app()
|
app()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|||||||
43
solutions/0037.TruncatablePrimes/euler_37.py
Normal file
43
solutions/0037.TruncatablePrimes/euler_37.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
"""
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from itertools import product
|
||||||
|
|
||||||
|
|
||||||
|
def combine_lists(a: list[int], b: list[int|None], c: list[int]) -> list[int]:
|
||||||
|
"""将三个列表的每个元素组合成数字"""
|
||||||
|
return [int(f"{x}{y}{z}") for x, y, z in product(a, b, c)]
|
||||||
|
|
||||||
|
|
||||||
|
def is_prime(n: int) -> bool:
|
||||||
|
"""判断一个数是否为素数"""
|
||||||
|
if n < 2:
|
||||||
|
return False
|
||||||
|
for i in range(2, int(n ** 0.5) + 1):
|
||||||
|
if n % i == 0:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def TruncatablePrime() -> list[int]:
|
||||||
|
begin = [2, 3, 5, 7]
|
||||||
|
end = [3, 7]
|
||||||
|
middle = [1, 3, 7, 9]
|
||||||
|
res = []
|
||||||
|
for length in range(2, 7):
|
||||||
|
if length - 2 == 0:
|
||||||
|
midb = []
|
||||||
|
else:
|
||||||
|
midb = product(middle, repeat=length - 2)
|
||||||
|
midb = [int("".join(map(str, x))) for x in midb]
|
||||||
|
nums = combine_lists(begin, midb, end)
|
||||||
|
for num in nums :
|
||||||
|
if is
|
||||||
171
solutions/0037.TruncatablePrimes/euler_37_primeclass.py
Normal file
171
solutions/0037.TruncatablePrimes/euler_37_primeclass.py
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
"""
|
||||||
|
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()
|
||||||
BIN
solutions/0037.TruncatablePrimes/millerrabin_test.pdf
Normal file
BIN
solutions/0037.TruncatablePrimes/millerrabin_test.pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user