136 lines
3.2 KiB
Python
136 lines
3.2 KiB
Python
"""
|
||
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互质的d,10^(φ(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)
|