✨ feat(0029.DistinctPowers):添加欧拉项目第29题解决方案
📝 docs(0029.DistinctPowers):添加问题描述和解题思路文档 ✨ feat(euler_29.py):实现基础解决方案和高效算法 ✨ feat(euler_29_best.py):实现基于容斥原理的最优解决方案
This commit is contained in:
10
solutions/0029.DistinctPowers/README.md
Normal file
10
solutions/0029.DistinctPowers/README.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Distinct Powers
|
||||||
|
|
||||||
|
只需要比较所有可能重复的底和幂,找到有多少这样的a^b就能知道有多少重复。
|
||||||
|
这个逻辑最为简单,我自己的实现免费处理较大额的底和幂的情况,这点我暂时没想到好方法。
|
||||||
|
|
||||||
|
当看到 [WP(Page 5)](https://projecteuler.net/post_id=92910) 的方法,我才明白自己的问题在哪。
|
||||||
|
这类问题真的是,单纯解出来不算什么,如何使用数学更简单更快捷的解出来,才是难的。
|
||||||
|
|
||||||
|
核心关键点是组合数学的[容斥原理](https://zh.wikipedia.org/wiki/%E6%8E%92%E5%AE%B9%E5%8E%9F%E7%90%86)。
|
||||||
|
因为幂的数学特点,可能需要多次应用容斥原理,以确保不重复计算。这也是我自己方法和WP方法的差距所在。
|
||||||
92
solutions/0029.DistinctPowers/euler_29.py
Normal file
92
solutions/0029.DistinctPowers/euler_29.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
"""
|
||||||
|
Consider all integer combinations of a^b for 2<=a<=5 and 2<=b<=5 :
|
||||||
|
|
||||||
|
2^2 = 4 2^3 = 8 2^4 = 16 2^5 = 32
|
||||||
|
3^2 = 9 3^3 = 27 3^4 = 81 3^5 = 243
|
||||||
|
4^2 = 16 4^3 = 64 4^4 = 256 4^5 = 1024
|
||||||
|
5^2 = 25 5^3 = 125 5^4 = 625 5^5 = 3125
|
||||||
|
|
||||||
|
If they are then placed in numerical order, with any repeats removed,
|
||||||
|
we get the following sequence of 15 distinct terms:
|
||||||
|
|
||||||
|
4, 8, 9, 16, 25, 27, 32, 64, 81, 125, 243, 256, 625, 1024, 3125
|
||||||
|
|
||||||
|
How many distinct terms are in the sequence generated by a^b for 2<=a<=100 and 2<=b<=100 ?
|
||||||
|
"""
|
||||||
|
|
||||||
|
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__} runtime: {end_time - start_time} seconds")
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def count_distinct_terms_efficient(limit_a, limit_b):
|
||||||
|
# 预计算每个 a 的最小底数表示
|
||||||
|
base_map = {}
|
||||||
|
power_map = {}
|
||||||
|
|
||||||
|
for a in range(2, limit_a + 1):
|
||||||
|
base_map[a] = a
|
||||||
|
power_map[a] = 1
|
||||||
|
|
||||||
|
# 找出所有可以表示为幂的数
|
||||||
|
for power in range(2, 7): # 2^7=128>100,所以最多到6次幂
|
||||||
|
base = 2
|
||||||
|
while base**power <= limit_a:
|
||||||
|
value = base**power
|
||||||
|
# 如果这个值还没有被更小的底数表示
|
||||||
|
if base_map[value] == value:
|
||||||
|
base_map[value] = base
|
||||||
|
power_map[value] = power
|
||||||
|
base += 1
|
||||||
|
|
||||||
|
# 使用集合存储 (base, exponent) 对
|
||||||
|
seen = set()
|
||||||
|
|
||||||
|
for a in range(2, limit_a + 1):
|
||||||
|
base = base_map[a]
|
||||||
|
power = power_map[a]
|
||||||
|
|
||||||
|
# 如果 a 是某个数的幂,我们需要检查 base 是否还能分解
|
||||||
|
# 例如:16 = 4^2,但 4 = 2^2,所以 16 = 2^4
|
||||||
|
# 我们需要找到最终的 base
|
||||||
|
while base_map[base] != base:
|
||||||
|
power = power * power_map[base]
|
||||||
|
base = base_map[base]
|
||||||
|
|
||||||
|
for b in range(2, limit_b + 1):
|
||||||
|
seen.add((base, power * b))
|
||||||
|
|
||||||
|
return len(seen)
|
||||||
|
|
||||||
|
|
||||||
|
def do_compute(a_top: int, b_top: int, a_down: int = 2, b_down: int = 2) -> int:
|
||||||
|
tmp = set()
|
||||||
|
for a in range(a_down, a_top + 1):
|
||||||
|
for b in range(b_down, b_top + 1):
|
||||||
|
tmp.add(a**b)
|
||||||
|
return len(tmp)
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main():
|
||||||
|
"""not bad"""
|
||||||
|
print(count_distinct_terms_efficient(100, 100))
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main2():
|
||||||
|
print(do_compute(100, 100))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
main2()
|
||||||
134
solutions/0029.DistinctPowers/euler_29_best.py
Normal file
134
solutions/0029.DistinctPowers/euler_29_best.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import math
|
||||||
|
import time
|
||||||
|
from functools import lru_cache
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
|
||||||
|
def timer(func: Callable) -> Callable:
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
start_time = time.perf_counter()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end_time = time.perf_counter()
|
||||||
|
elapsed = end_time - start_time
|
||||||
|
print(f"Function {func.__name__} execution time: {elapsed:.4f} seconds")
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def maxpower(a: int, n: int) -> int:
|
||||||
|
"""计算最大的整数c,使得a^c ≤ n"""
|
||||||
|
res = int(math.log(n) / math.log(a))
|
||||||
|
|
||||||
|
# 处理边界情况
|
||||||
|
if pow(a, res + 1) <= n:
|
||||||
|
res += 1
|
||||||
|
if pow(a, res) > n:
|
||||||
|
res -= 1
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def lcm(a: int, b: int) -> int:
|
||||||
|
"""计算最小公倍数(使用缓存优化)"""
|
||||||
|
gcd_val = math.gcd(a, b)
|
||||||
|
return a // gcd_val * b
|
||||||
|
|
||||||
|
|
||||||
|
def recurse(
|
||||||
|
lc: int, index: int, sign: int, left: int, right: int, thelist: list[int]
|
||||||
|
) -> int:
|
||||||
|
"""容斥原理的递归实现"""
|
||||||
|
if lc > right:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
res = sign * (right // lc - (left - 1) // lc)
|
||||||
|
|
||||||
|
# 递归处理剩余元素
|
||||||
|
for i in range(index + 1, len(thelist)):
|
||||||
|
res += recurse(lcm(lc, thelist[i]), i, -sign, left, right, thelist)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def dd(left: int, right: int, a: int, b: int, check: list[bool]) -> int:
|
||||||
|
"""双层容斥计算"""
|
||||||
|
res = right // b - (left - 1) // b
|
||||||
|
thelist = [i for i in range(a, b) if check[i]]
|
||||||
|
|
||||||
|
for i in range(len(thelist)):
|
||||||
|
res -= recurse(lcm(b, thelist[i]), i, 1, left, right, thelist)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def compute_counts(n: int) -> tuple[list[int], int, int]:
|
||||||
|
"""计算前缀和数组"""
|
||||||
|
sqn = int(math.isqrt(n)) # 使用isqrt替代sqrt,返回整数
|
||||||
|
maxc = maxpower(2, n)
|
||||||
|
|
||||||
|
# 初始化数组
|
||||||
|
counts = [0] * (maxc + 1)
|
||||||
|
counts[1] = n - 1
|
||||||
|
|
||||||
|
# 主计算循环
|
||||||
|
for c in range(2, maxc + 1):
|
||||||
|
check = [True] * (maxc + 1)
|
||||||
|
umin = (c - 1) * n + 1
|
||||||
|
umax = c * n
|
||||||
|
|
||||||
|
# 优化筛法:使用步长跳过非倍数
|
||||||
|
for i in range(c, maxc // 2 + 1):
|
||||||
|
check[i * 2 : maxc + 1 : i] = [False] * ((maxc - i * 2) // i + 1)
|
||||||
|
|
||||||
|
# 只处理质数(check[f]为True)
|
||||||
|
for f in range(c, maxc + 1):
|
||||||
|
if check[f]:
|
||||||
|
counts[f] += dd(umin, umax, c, f, check)
|
||||||
|
|
||||||
|
# 计算前缀和
|
||||||
|
for c in range(2, maxc + 1):
|
||||||
|
counts[c] += counts[c - 1]
|
||||||
|
|
||||||
|
return counts, sqn, maxc
|
||||||
|
|
||||||
|
|
||||||
|
def compute_final_answer(n: int) -> int:
|
||||||
|
"""计算最终答案"""
|
||||||
|
counts, sqn, _ = compute_counts(n)
|
||||||
|
|
||||||
|
ans = 0
|
||||||
|
coll = 0
|
||||||
|
used = [False] * (sqn + 1)
|
||||||
|
|
||||||
|
# 统计答案
|
||||||
|
for i in range(2, sqn + 1):
|
||||||
|
if not used[i]:
|
||||||
|
c = maxpower(i, n)
|
||||||
|
ans += counts[c]
|
||||||
|
|
||||||
|
u = i
|
||||||
|
for j in range(2, c + 1):
|
||||||
|
u *= i
|
||||||
|
if u <= sqn:
|
||||||
|
used[u] = True
|
||||||
|
else:
|
||||||
|
coll += c - j + 1
|
||||||
|
break
|
||||||
|
|
||||||
|
# 最终调整
|
||||||
|
ans += (n - sqn) * (n - 1)
|
||||||
|
ans -= coll * (n - 1)
|
||||||
|
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main(n: int = 10**6) -> None:
|
||||||
|
answer = compute_final_answer(n)
|
||||||
|
print(f"n = {n}, Answer = {answer}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(10**7)
|
||||||
Reference in New Issue
Block a user