Files

99 lines
3.4 KiB
Python

"""
If we take 47, reverse and add, 47 + 74 = 121, which is palindromic.
Not all numbers produce palindromes so quickly. For example,
349 + 943 = 1292
1292 + 2921 = 4213
4213 + 3124 = 7337
That is, 349 took three iterations to arrive at a palindrome.
Although no one has proved it yet, it is thought that some numbers, like 196,
never produce a palindrome. A number that never forms a palindrome through the
reverse and add process is called a Lychrel number. Due to the theoretical nature
of these numbers, and for the purpose of this problem, we shall assume that
a number is Lychrel until proven otherwise. In addition you are given that for
every number below ten-thousand, it will either (i) become a palindrome
in less than fifty iterations, or, (ii) no one, with all the computing power that exists,
has managed so far to map it to a palindrome. In fact, 10677 is the first number to
be shown to require over fifty iterations before producing a palindrome:
4668731596684224866951378664 (53 iterations, 28-digits).
Surprisingly, there are palindromic numbers that are themselves Lychrel numbers;
the first example is 4994.
How many Lychrel numbers are there below ten-thousand?
NOTE: Wording was modified slightly on 24 April 2007 to emphasise the theoretical nature of Lychrel numbers.
"""
import time
from functools import wraps
from typing import Any, Callable, TypeVar
F = TypeVar("F", bound=Callable[..., Any])
def benchmark(repeat: int = 1) -> Callable[[F], F]:
"""
重复运行目标函数并计算平均耗时。
平均耗时会被存储在 wrapper.avg_time 和 wrapper.total_time 中,
同时会打印到控制台。函数的返回值不受影响。
"""
if repeat < 1:
raise ValueError("repeat 必须 >= 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__} | 重复 {repeat} 次 | "
f"平均: {wrapper.avg_time:.6f}s | 总计: {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 is_palindrome(n):
return str(n) == str(n)[::-1]
def is_lychrel(n, max_iterations=50):
for _ in range(max_iterations):
n += int(str(n)[::-1])
if is_palindrome(n):
return False
return True
@benchmark(repeat=15)
def count_lychrel_numbers(limit):
count = 0
for n in range(1, limit + 1):
if is_lychrel(n):
count += 1
return count
if __name__ == "__main__":
result = count_lychrel_numbers(10000)
print(f"Number of Lychrel numbers below 10,000: {result}")