✨ 新增欧拉第34、35题解法及说明
This commit is contained in:
206
solutions/0035.CircularPrimes/euler_35.py
Normal file
206
solutions/0035.CircularPrimes/euler_35.py
Normal file
@@ -0,0 +1,206 @@
|
||||
"""
|
||||
The number, 179, is called a circular prime because all rotations of the digits: 179, 791, and 917,
|
||||
are themselves prime.
|
||||
|
||||
There are thirteen such primes below 100 : 2,3,5,7,11,13,17,31,37,71,73,79, and 97.
|
||||
|
||||
How many circular primes are there below one million?
|
||||
"""
|
||||
|
||||
import random
|
||||
import time
|
||||
from itertools import permutations
|
||||
from sqlite3.dbapi2 import Time
|
||||
from typing import Union
|
||||
|
||||
# 预计算的小素数集合,用于快速排除
|
||||
_SMALL_PRIMES = frozenset((2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37))
|
||||
|
||||
|
||||
def timer(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
start_time = time.time()
|
||||
result = func(*args, **kwargs)
|
||||
end_time = time.time()
|
||||
print(f"{func.__name__} time: {end_time - start_time:.6f} seconds")
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def sieve_best(n: int) -> list[int]:
|
||||
"""返回 [2..n) 内所有素数"""
|
||||
if n <= 2:
|
||||
return []
|
||||
size = (n - 1) // 2
|
||||
comp = bytearray(size)
|
||||
limit = int(n**0.5) // 2
|
||||
for i in range(1, limit + 1):
|
||||
if not comp[i]:
|
||||
p = 2 * i + 1
|
||||
s = (p * p - 1) // 2
|
||||
comp[s::p] = b"\1" * ((size - s - 1) // p + 1)
|
||||
res = [2, *(2 * i + 1 for i, v in enumerate(comp) if not v)]
|
||||
res.sort()
|
||||
return [i for i in res if i != 1]
|
||||
|
||||
|
||||
def is_circular(n: int, primes: list[int]) -> bool:
|
||||
if n < 2:
|
||||
return False
|
||||
if n in [2, 3, 5, 7, 11]:
|
||||
return True
|
||||
digits = str(n)
|
||||
allnum = [digits[i:] + digits[:i] for i in range(len(digits))]
|
||||
for num in allnum:
|
||||
if int(num) not in primes:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_probable_prime(n: Union[int, str], k: int = 20) -> bool:
|
||||
"""
|
||||
Miller-Rabin素性检测算法
|
||||
|
||||
参数:
|
||||
n: 待检测的整数(支持int或数字字符串)
|
||||
k: 测试次数,误差率约为4^(-k),默认20次(误差率约9e-13)
|
||||
|
||||
返回:
|
||||
bool: 如果n很可能是素数返回True,否则返回False
|
||||
"""
|
||||
# 支持字符串输入
|
||||
if isinstance(n, str):
|
||||
n = int(n)
|
||||
|
||||
# 基本检查
|
||||
if n < 2:
|
||||
return False
|
||||
if n in _SMALL_PRIMES:
|
||||
return True
|
||||
|
||||
# 检查小素数的倍数
|
||||
for p in _SMALL_PRIMES:
|
||||
if n % p == 0:
|
||||
return False
|
||||
|
||||
# 特殊情况:偶数(已排除2)
|
||||
if n % 2 == 0:
|
||||
return False
|
||||
|
||||
# 将 n-1 写成 2^s * d 的形式
|
||||
d = n - 1
|
||||
s = 0
|
||||
while d % 2 == 0:
|
||||
d //= 2
|
||||
s += 1
|
||||
|
||||
# 边界:如果 n-1 可以直接作为基数,避免随机数生成问题
|
||||
if n < 4:
|
||||
return n in (2, 3)
|
||||
|
||||
# Miller-Rabin测试
|
||||
for _ in range(k):
|
||||
# 生成随机基数,范围 [2, n-2]
|
||||
a = random.randrange(2, n - 1)
|
||||
|
||||
# 计算 x = a^d mod n
|
||||
x = pow(a, d, n)
|
||||
|
||||
# 如果 x == 1 或 x == n-1,通过本轮测试
|
||||
if x == 1 or x == n - 1:
|
||||
continue
|
||||
|
||||
# 重复平方检查
|
||||
for _ in range(s - 1):
|
||||
x = pow(x, 2, n)
|
||||
if x == n - 1:
|
||||
break
|
||||
else:
|
||||
# 所有平方后都没有得到 n-1,n是合数
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# 优化的确定素数版本(适用于小整数)
|
||||
def is_prime_small(n: int) -> bool:
|
||||
"""确定性素数检测,适用于n < 2^64"""
|
||||
if n < 2:
|
||||
return False
|
||||
if n % 2 == 0:
|
||||
return n == 2
|
||||
if n % 3 == 0:
|
||||
return n == 3
|
||||
|
||||
# 检查小素数
|
||||
for p in _SMALL_PRIMES:
|
||||
if n % p == 0:
|
||||
return n == p
|
||||
|
||||
# 6k±1 优化检查
|
||||
i = 5
|
||||
w = 2
|
||||
while i * i <= n:
|
||||
if n % i == 0:
|
||||
return False
|
||||
i += w
|
||||
w = 6 - w # 在2和4之间切换:5,7,11,13,17,19...
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# 智能选择函数
|
||||
def is_prime(n: Union[int, str], k: int = 20) -> bool:
|
||||
"""
|
||||
智能素数检测:对小整数使用确定性算法,对大整数使用Miller-Rabin
|
||||
|
||||
参数:
|
||||
n: 待检测的整数
|
||||
k: Miller-Rabin测试次数(仅对大整数有效)
|
||||
|
||||
返回:
|
||||
bool: 如果是素数返回True
|
||||
"""
|
||||
if isinstance(n, str):
|
||||
n = int(n)
|
||||
|
||||
# 小整数使用确定性算法
|
||||
if n < 2**64:
|
||||
return is_prime_small(n)
|
||||
|
||||
# 大整数使用Miller-Rabin
|
||||
return is_probable_prime(n, k)
|
||||
|
||||
|
||||
@timer
|
||||
def main_quick(max: int) -> None:
|
||||
res = set()
|
||||
for i in range(2, max):
|
||||
if is_prime(i):
|
||||
if i in res:
|
||||
continue
|
||||
else:
|
||||
s = str(i)
|
||||
allnums = [s[i:] + s[:i] for i in range(len(s))]
|
||||
flag = 0
|
||||
for j in allnums:
|
||||
num = int("".join(j))
|
||||
if is_prime(num):
|
||||
flag += 1
|
||||
if flag == len(allnums):
|
||||
res.update(allnums)
|
||||
print(len(res))
|
||||
|
||||
|
||||
@timer
|
||||
def main(n: int) -> None:
|
||||
primes = sieve_best(n)
|
||||
circular_primes = [p for p in primes if is_circular(p, primes)]
|
||||
print(len(circular_primes))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
num = 1000000
|
||||
main(num)
|
||||
main_quick(num)
|
||||
Reference in New Issue
Block a user