diff --git a/main.py b/main.py index 7cf97f7..5af3383 100644 --- a/main.py +++ b/main.py @@ -64,5 +64,9 @@ def version(): typer.echo("projecteuler solution version 0.1.0") -if __name__ == "__main__": +def main(): app() + + +if __name__ == "__main__": + main() diff --git a/solutions/0037.TruncatablePrimes/euler_37.py b/solutions/0037.TruncatablePrimes/euler_37.py new file mode 100644 index 0000000..377e9c2 --- /dev/null +++ b/solutions/0037.TruncatablePrimes/euler_37.py @@ -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 diff --git a/solutions/0037.TruncatablePrimes/euler_37_primeclass.py b/solutions/0037.TruncatablePrimes/euler_37_primeclass.py new file mode 100644 index 0000000..b49207e --- /dev/null +++ b/solutions/0037.TruncatablePrimes/euler_37_primeclass.py @@ -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() diff --git a/solutions/0037.TruncatablePrimes/millerrabin_test.pdf b/solutions/0037.TruncatablePrimes/millerrabin_test.pdf new file mode 100644 index 0000000..327d084 Binary files /dev/null and b/solutions/0037.TruncatablePrimes/millerrabin_test.pdf differ