Files
SolutionEuler/solutions/0000_0029/0026.ReciprocalCycles/euler_26.py
2025-12-26 17:35:14 +08:00

136 lines
3.2 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.

"""
A unit fraction contains 1in the numerator.
The decimal representation of the unit fractions with denominators 2 to 10 are given:
1/2 = 0.5
1/3 = 0.(3)
1/4 = 0.25
1/5 = 0.2
1/6 = 0.1(6)
1/7 = 0.(142857)
1/8 = 0.125
1/9 = 0.(1)
1/10 = 0.1
Where 0.1(6) means 0.166666..., and has a 1-digit recurring cycle.
It can be seen that 1/7 has a 6-digit recurring cycle.
Find the value of d < 1000 for which 1/d contains the longest recurring cycle in its decimal fraction part.
"""
import random
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
def repeating_cycle_fermat(d: int) -> int:
# 移除因子2和5
while d % 2 == 0:
d //= 2
while d % 5 == 0:
d //= 5
if d == 1:
return 0
# 费马小定理对于与10互质的d10^(φ(d)) ≡ 1 mod d
# 循环节长度一定是φ(d)的约数
# 计算欧拉函数φ(d)
phi = d
n = d
# 质因数分解求φ(d)
p = 2
while p * p <= n:
if n % p == 0:
phi -= phi // p
while n % p == 0:
n //= p
p += 1 if p == 2 else 2 # 从2开始然后检查奇数
if n > 1:
phi -= phi // n
# 现在找φ的最小约数k使得10^k ≡ 1 mod d
min_cycle = phi
for k in range(1, int(phi**0.5) + 1):
if phi % k == 0:
if pow(10, k, d) == 1:
return k
other = phi // k
if pow(10, other, d) == 1 and other < min_cycle:
min_cycle = other
return min_cycle
@timer
def main_searchall(n: int) -> None:
max_cycle = 0
max_d = 0
for d in range(1, n):
cycle = repeating_cycle_fermat(d)
if cycle > max_cycle:
max_cycle = cycle
max_d = d
print(f"{max_d} have {max_cycle} digits in its decimal fraction part.")
def is_probable_prime(n: int, trials: int = 20) -> bool:
"""Miller-Rabin素性测试快速判断是否为质数"""
if n < 2:
return False
if n in (2, 3):
return True
if n % 2 == 0:
return False
# 将 n-1 写成 d * 2^s 的形式
d = n - 1
s = 0
while d % 2 == 0:
d //= 2
s += 1
# 测试
for _ in range(trials):
a = random.randrange(2, n - 1)
x = pow(a, d, n)
if x == 1 or x == n - 1:
continue
for _ in range(s - 1):
x = pow(x, 2, n)
if x == n - 1:
break
else:
return False
return True
@timer
def main_by_prime(n: int) -> None:
for d in range(n, 0, -1):
max_cycle = d - 1
max_d = d
cycle = repeating_cycle_fermat(d)
if max_cycle == cycle:
print(f"{max_d} have {max_cycle} digits in its decimal fraction part.")
return None
if __name__ == "__main__":
n = 10000
main_searchall(n)
main_by_prime(n)