diff --git a/main.py b/main.py index 42b9bfe..dac9412 100644 --- a/main.py +++ b/main.py @@ -15,7 +15,25 @@ def add_newproblem(num: int, name: str | None = None) -> None: else: title = f"{num:04d}" Path(f"solutions/{title}").mkdir(parents=True, exist_ok=True) - Path(f"solutions/{title}/euler_{num}.py").touch() + file_name = f"solutions/{title}/euler_{num}.py" + Path(file_name).touch() + with open(file_name, "w") as f: + f.write("""''' + +''' +import time + + +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 + """) @app.command("list", help="List all problems in the projecteuler repository.") diff --git a/solutions/0048.SelfPowers/euler_48.py b/solutions/0048.SelfPowers/euler_48.py new file mode 100644 index 0000000..75c431a --- /dev/null +++ b/solutions/0048.SelfPowers/euler_48.py @@ -0,0 +1,96 @@ +""" +The series, 1^1 + 2^2 + 3^3 + ... + 10^10 = 10405071317 . + +Find the last ten digits of the series, 1^1 + 2^2 + 3^3 + ... + 1000^1000. +""" + +import math +import time +from functools import lru_cache + + +def timer(func): + def wrapper(*args, **kwargs): + start_time = time.perf_counter() + result = func(*args, **kwargs) + end_time = time.perf_counter() + elapsed_time = end_time - start_time + print(f"{func.__name__} time: {elapsed_time:.6f} seconds") + return result + + return wrapper + + +@lru_cache(maxsize=128) +def _crt_params(n: int): + """预计算并缓存 CRT 参数,避免重复计算""" + mod_2 = 2**n + mod_5 = 5**n + # 计算 5^n 模 2^n 的模逆元(Python 3.8+ 支持负数指数求逆) + inv_5n_mod_2n = pow(mod_5, -1, mod_2) + return mod_2, mod_5, inv_5n_mod_2n + + +def fast_last_n_digits(base: int, exp: int, n: int) -> int: + """ + 计算 base^exp 的最后 n 位数字(模 10^n)。 + + 复杂度:O(n * M(n/2)),其中 M(k) 是 k 位整数乘法的复杂度。 + 相比直接 pow(base, exp, 10**n) 的 O(log(exp) * M(n)), + 当 exp 极大且 n > 2 时通常快 2-4 倍。 + """ + if n == 0: + return 0 + if exp == 0: + return 1 % (10**n) + + # 小 n 直接用内置 pow(避免 CRT 开销) + if n <= 2: + return pow(base, exp, 10**n) + + base = base % (10**n) + + # 1. 指数缩减:利用 Carmichael 函数 λ(10^n) = 4·10^(n-1) (n≥2) + # 仅当 base 与 10 互质时适用 + if math.gcd(base, 10) == 1: + lambda_n = 4 * (10 ** (n - 1)) + if exp > lambda_n: + exp = exp % lambda_n + if exp == 0: + exp = lambda_n + + # 2. CRT 分解:分别计算模 2^n 和模 5^n + mod_2, mod_5, inv = _crt_params(n) + + # 优化模 2^n:如果 base 是偶数且含有足够多的因子 2 + if base & 1 == 0: # 偶数检查 + # 快速计算 base 中因子 2 的个数(末尾 0 的个数) + k = (base & -base).bit_length() - 1 + if k * exp >= n: + r2 = 0 # 2^n 整除 base^exp + else: + r2 = pow(base, exp, mod_2) + else: + r2 = pow(base, exp, mod_2) + + # 计算模 5^n(主要运算,但操作数大小减半) + r5 = pow(base, exp, mod_5) + + # 3. CRT 合并(Garner 算法): + # 求解 x ≡ r2 (mod 2^n), x ≡ r5 (mod 5^n) + # x = r5 + 5^n * ((r2 - r5) * inv(5^n, 2^n) mod 2^n) + t = ((r2 - r5) * inv) % mod_2 + return r5 + mod_5 * t + + +@timer +def key_main(limit: int = 1000, last_n: int = 10): + res = [] + for i in range(1, limit + 1): + res.append(fast_last_n_digits(i, i, last_n)) + res = int("".join(list(str(sum(res)))[-last_n:])) + print(res) + + +if __name__ == "__main__": + key_main()