✨ 新增欧拉第34、35题解法及说明
This commit is contained in:
67
solutions/0034/euler_34.py
Normal file
67
solutions/0034/euler_34.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
"""
|
||||||
|
145 is a curious number, as 1! + 4! + 5! = 1 + 24 + 120 = 145.
|
||||||
|
|
||||||
|
Find the sum of all numbers which are equal to the sum of the factorial of their digits.
|
||||||
|
|
||||||
|
Note: As 1!=1 and 2!=2 are not sums they are not included.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def factorial(n: int) -> int:
|
||||||
|
if n == 0:
|
||||||
|
return 1
|
||||||
|
elif n == 1:
|
||||||
|
return 1
|
||||||
|
elif n == 2:
|
||||||
|
return 2
|
||||||
|
elif n == 3:
|
||||||
|
return 6
|
||||||
|
elif n == 4:
|
||||||
|
return 24
|
||||||
|
elif n == 5:
|
||||||
|
return 120
|
||||||
|
elif n == 6:
|
||||||
|
return 720
|
||||||
|
elif n == 7:
|
||||||
|
return 5040
|
||||||
|
elif n == 8:
|
||||||
|
return 40320
|
||||||
|
elif n == 9:
|
||||||
|
return 362880
|
||||||
|
elif n == 10:
|
||||||
|
return 3628800
|
||||||
|
else:
|
||||||
|
return n * factorial(n - 1)
|
||||||
|
|
||||||
|
|
||||||
|
def sum_digits(n: int) -> int:
|
||||||
|
tmp = str(n)
|
||||||
|
return sum(factorial(int(digit)) for digit in tmp)
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main() -> None:
|
||||||
|
res = list()
|
||||||
|
for i in range(12, 9999999):
|
||||||
|
if sum_digits(i) == i:
|
||||||
|
res.append(i)
|
||||||
|
print(sum(res))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
8
solutions/0034/readme.md
Normal file
8
solutions/0034/readme.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
参考 [Factorion](https://mathworld.wolfram.com/Factorion.html)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
本来想的是,是否可以确定满足这个规则的数列上限。口算估计是999999,然后就是暴力计算了。
|
||||||
|
看到结果,则比较让我惊讶,这样的数只有两个:145和40585。
|
||||||
|
|
||||||
|
再加上看别人的思考,从我个人来说,可能不是所有问题都适合这个思路?
|
||||||
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)
|
||||||
80
solutions/0035.CircularPrimes/euler_35_better.py
Normal file
80
solutions/0035.CircularPrimes/euler_35_better.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
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__} took {end_time - start_time:.6f} seconds")
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- 1. PRIMES ----------
|
||||||
|
MAX = 1_000_000
|
||||||
|
is_p = bytearray(b"\1") * MAX # 1 表示素数,0 表示合数(与原来相反,方便理解)
|
||||||
|
is_p[0] = is_p[1] = 0 # 0和1不是素数
|
||||||
|
|
||||||
|
# 优化:使用标准埃拉托斯特尼筛法
|
||||||
|
for p in range(2, int(MAX**0.5) + 1):
|
||||||
|
if is_p[p]:
|
||||||
|
is_p[p * p : MAX : p] = b"\0" * ((MAX - p * p - 1) // p + 1)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- 2. SCROLLING ----------
|
||||||
|
def rotations(n: int) -> list[int]:
|
||||||
|
"""返回 n 的所有旋转(数学移位版,无字符串开销)"""
|
||||||
|
if n < 10:
|
||||||
|
return [n]
|
||||||
|
s = str(n)
|
||||||
|
k = len(s)
|
||||||
|
res = []
|
||||||
|
for i in range(k):
|
||||||
|
# 使用切片旋转,避免重复计算
|
||||||
|
rotated = int(s[i:] + s[:i])
|
||||||
|
res.append(rotated)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- 3. MAIN ----------
|
||||||
|
@timer
|
||||||
|
def main(limit: int = MAX) -> int:
|
||||||
|
# 预过滤:只要含 0,2,4,5,6,8 就可以跳过(除了2和5本身)
|
||||||
|
bad_digits = set("024568")
|
||||||
|
total = 0
|
||||||
|
counted = set() # 用于去重,避免重复计数
|
||||||
|
|
||||||
|
for p in range(2, limit):
|
||||||
|
if is_p[p]: # p是素数
|
||||||
|
# 快速数字过滤(2和5是特例)
|
||||||
|
if p != 2 and p != 5:
|
||||||
|
s = str(p)
|
||||||
|
if not bad_digits.isdisjoint(s):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 如果已经统计过,跳过
|
||||||
|
if p in counted:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 检查所有旋转
|
||||||
|
rs = rotations(p)
|
||||||
|
|
||||||
|
# 检查所有旋转数是否都是素数
|
||||||
|
valid = True
|
||||||
|
for r in rs:
|
||||||
|
if r >= limit or not is_p[r]:
|
||||||
|
valid = False
|
||||||
|
break
|
||||||
|
|
||||||
|
if valid:
|
||||||
|
# 如果是循环素数,统计并标记已计数
|
||||||
|
total += len(set(rs)) # 使用set避免重复旋转
|
||||||
|
counted.update(rs)
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(f"循环素数数量: {main()}")
|
||||||
6
solutions/0035.CircularPrimes/readme.md
Normal file
6
solutions/0035.CircularPrimes/readme.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# 判断素数
|
||||||
|
|
||||||
|
这个问题也很有和判断素数,以及如何更好的记录判断结果。
|
||||||
|
当然,如何更快计算出滚动的数字也很重要。
|
||||||
|
|
||||||
|
个人感觉这是个算法问题更多的问题,数学占比并不多。
|
||||||
Reference in New Issue
Block a user