Files
SolutionEuler/solutions/0035.CircularPrimes/euler_35.py

207 lines
4.9 KiB
Python
Raw 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, 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-1n是合数
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)