90 lines
2.9 KiB
Python
90 lines
2.9 KiB
Python
'''
|
|
It is possible to show that the square root of two can be expressed as an infinite continued fraction.
|
|
sqrt{2}=1+frac{1}{2+frac{1}{2+frac{1}{2+...}}}
|
|
|
|
By expanding this for the first four iterations, we get:
|
|
begin{aligned}
|
|
& 1+frac{1}{2}=frac{3}{2}=1.5 \\
|
|
& 1+frac{1}{2+frac{1}{2}}=frac{7}{5}=1.4 \\
|
|
& 1+frac{1}{2+frac{1}{2+frac{1}{2}}}=frac{17}{12}=1.41666... \\
|
|
& 1+frac{1}{2+frac{1}{2+frac{1}{2+frac{1}{2}}}}=frac{41}{29}=1.41379 ldots
|
|
end{aligned}
|
|
|
|
The next three expansions are 99/70, 239/169, and 577/408, but the eighth expansion, 1393/985,
|
|
is the first example where the number of digits in the numerator exceeds the number of digits in the denominator.
|
|
|
|
In the first one-thousand expansions, how many fractions contain a numerator with more digits than the denominator?
|
|
'''
|
|
import time
|
|
from functools import wraps
|
|
from typing import Any, Callable, TypeVar
|
|
|
|
from math import gcd
|
|
|
|
F = TypeVar("F", bound=Callable[..., Any])
|
|
|
|
|
|
def benchmark(repeat: int = 1) -> Callable[[F], F]:
|
|
if repeat < 1:
|
|
raise ValueError("repeat must >= 1")
|
|
|
|
def decorator(func: F) -> F:
|
|
@wraps(func)
|
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
total = 0.0
|
|
result = None
|
|
|
|
for _ in range(repeat):
|
|
start = time.perf_counter()
|
|
result = func(*args, **kwargs)
|
|
end = time.perf_counter()
|
|
total += end - start
|
|
|
|
wrapper.avg_time = total / repeat # type: ignore[attr-defined]
|
|
wrapper.total_time = total # type: ignore[attr-defined]
|
|
|
|
print(
|
|
f"[Benchmark] {func.__name__} | repeated {repeat} times | "
|
|
f"average: {wrapper.avg_time:.6f}s | total: {wrapper.total_time:.6f}s" # type: ignore[attr-defined]
|
|
)
|
|
return result
|
|
|
|
wrapper.avg_time = 0.0 # type: ignore[attr-defined]
|
|
wrapper.total_time = 0.0 # type: ignore[attr-defined]
|
|
return wrapper # type: ignore[return-value]
|
|
|
|
return decorator
|
|
|
|
|
|
def compute_square_root(n: int = 1000) -> list[tuple[int,int]] :
|
|
"""
|
|
Compute the square root of 2 up to n expansions.
|
|
"""
|
|
cf = [1] + [2 for _ in range(0, n)]
|
|
p2, p1 = 0, 1 # p_{-2}, p_{-1}
|
|
q2, q1 = 1, 0 # q_{-2}, q_{-1}
|
|
results = []
|
|
for a in cf:
|
|
p = a * p1 + p2
|
|
q = a * q1 + q2
|
|
p2, p1 = p1, p
|
|
q2, q1 = q1, q
|
|
if gc := gcd(p, q) != 1:
|
|
p //= gc
|
|
q //= gc
|
|
results.append((p, q))
|
|
return results
|
|
|
|
@benchmark(20)
|
|
def main() -> None:
|
|
results = compute_square_root(1000)
|
|
count = 0
|
|
for p, q in results:
|
|
if len(str(p)) > len(str(q)):
|
|
count += 1
|
|
print(f"{count} fractions contain a numerator with more digits than the denominator.")
|
|
print(f"sqrt{2} = {results[-1][0]/results[-1][1]:.15f}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |