""" The prime 41, can be written as the sum of six consecutive primes: 41 = 2 + 3 + 5 + 7 + 11 + 13 This is the longest sum of consecutive primes that adds to a prime below one-hundred. The longest sum of consecutive primes below one-thousand that adds to a prime, contains 21 terms, and is equal to 953. Which prime, below one-million, can be written as the sum of the most consecutive primes? """ import time from math import isqrt, log from bitarray import bitarray def timer(func): def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() elapsed_time = end_time - start_time print(f"{func.__name__} time: {elapsed_time:.6f} seconds") return result return wrapper def primes_list(limit: int = 10**6) -> list[int]: if limit < 2: return [] # 初始化全1(假设都是素数),0和1置为0 is_prime = bitarray(limit + 1) is_prime.setall(True) is_prime[:2] = False # 0和1不是素数 # 只需筛到 sqrt(n) imax = isqrt(limit) + 1 for i in range(2, imax): if is_prime[i]: # 步长i,从i*i开始(小于i*i的已被更小的素数筛过) is_prime[i * i : limit + 1 : i] = False # 提取结果 return [i for i, val in enumerate(is_prime) if val] def get_bound(limit: int = 10**6) -> int: """ 使用牛顿法估算筛法所需的素数上限。 """ def f(t: float) -> float: return t**2 * (2 * log(t) - 1) - 4 * limit def fp(t: float) -> float: return 4 * t * log(t) x, y = limit, limit + 2 while y - x > 1: y, x = x, x - f(x) / fp(x) return int(x * (log(x) + log(log(x) - 1))) def max_primes_sum_best(limit: int = 10**6) -> tuple[int, int] | None: bound = get_bound(limit) primes = primes_list(bound) prime_set = set(primes) # === 2. 构建前缀和数组 === # prefix[i] 表示前 i 个素数的和(primes[0] 到 primes[i-1]) prefix = [0] current_sum = 0 max_possible_length = 0 # 计算从2开始连续累加,不超过 limit 的最大素数个数 for p in primes: current_sum += p if current_sum >= limit: break prefix.append(current_sum) max_possible_length += 1 # === 3. 搜索最长序列(倒序遍历 + 剪枝)=== best_length = 0 best_prime = 0 # 外层:右端点从大到小遍历(优先尝试更长序列) for right in range(max_possible_length, 0, -1): # 关键剪枝1:如果右端点 <= 当前最优长度,不可能找到更长序列 # 因为即使从左端点0开始,长度也只有 right,而 right <= best_length if right <= best_length: break # 关键剪枝2:左端点只需考虑到 (right - best_length - 1) # 因为我们需要的长度是 (right - left),必须满足 > best_length # 即 left < right - best_length max_left = right - best_length for left in range(max_left): # 计算连续素数之和:primes[left] 到 primes[right-1] consecutive_sum = prefix[right] - prefix[left] # 修正:如果 sum 已经超过 limit,left 继续增大 sum 会减小,所以不应 break # 但我们可以加一个判断:如果 prefix[right] - prefix[left] > limit,且 left 还在增大... # 实际上 left 增大,sum 减小,所以一旦 sum < limit,后续都 < limit # 简单处理:直接检查,不 break(或者可以预先判断,但为清晰起见省略) if consecutive_sum >= limit: continue # 跳过,但继续尝试更大的 left(sum 会变小) if consecutive_sum in prime_set: length = right - left if length > best_length: best_length = length best_prime = consecutive_sum # 更新剪枝边界:后续需要找比当前更长的,所以 max_left 可以缩小 # 但 Python 的 range 已经确定,我们只需依赖外层的 right <= best_length 判断 return best_prime, best_length @timer def main(): limit = int(input("limit:")) max_sum = max_primes_sum_best(limit) print(f"max primt: {max_sum}") if __name__ == "__main__": main()