From 1747152a4db00c751846536a1049baa5813e5659 Mon Sep 17 00:00:00 2001 From: SidneyZhang Date: Sun, 21 Dec 2025 17:45:44 +0800 Subject: [PATCH] solutions of problem 21 and problem 20 --- solutions/0020.FactorialDigitSum/euler_20.py | 67 +++++++++ solutions/0020.FactorialDigitSum/readme.md | 4 + solutions/0021.AmicableNumbers/euler_21.py | 96 +++++++++++++ solutions/0021.AmicableNumbers/readme.md | 138 +++++++++++++++++++ 4 files changed, 305 insertions(+) create mode 100644 solutions/0020.FactorialDigitSum/euler_20.py create mode 100644 solutions/0020.FactorialDigitSum/readme.md create mode 100644 solutions/0021.AmicableNumbers/euler_21.py create mode 100644 solutions/0021.AmicableNumbers/readme.md diff --git a/solutions/0020.FactorialDigitSum/euler_20.py b/solutions/0020.FactorialDigitSum/euler_20.py new file mode 100644 index 0000000..444cf07 --- /dev/null +++ b/solutions/0020.FactorialDigitSum/euler_20.py @@ -0,0 +1,67 @@ +""" +n! means `n! = n × (n-1) × ... 2 × 1`. +For example, 10! = 10 × 9 × ... × 2 × 1 = 3628800, +and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27 . + +Find the sum of the digits in the number 100!. +""" + +import math +import time +from functools import reduce + + +def timer(func): + def wrapper(*args, **kwargs): + start_time = time.time() + result = func(*args, **kwargs) + end_time = time.time() + print(f"Execution time: {end_time - start_time:.8f} seconds") + return result + + return wrapper + + +def large_multiplication(a: str, b: str) -> str: + result = [0] * (len(a) + len(b)) + for i in range(len(a) - 1, -1, -1): + carry = 0 + for j in range(len(b) - 1, -1, -1): + product = int(a[i]) * int(b[j]) + result[i + j + 1] + carry + result[i + j + 1] = product % 10 + carry = product // 10 + result[i] += carry + return "".join(map(str, result)).lstrip("0") or "0" + + +def factorial(n: int) -> int: + res = reduce(lambda x, y: x * y, range(1, n + 1)) + return res + + +@timer +def factorial_digit_sum(n: int) -> int: + if n >= 1000: + fact = math.factorial(n) + else: + fact = factorial(n) + return sum(int(digit) for digit in str(fact)) + + +@timer +def large_factorial(n: int) -> int: + result = "1" + for i in range(2, n + 1): + result = large_multiplication(result, str(i)) + print(f"{n}!={result}") + sum_all = sum(int(digit) for digit in result) + return sum_all + + +def main(): + print(factorial_digit_sum(100)) + print(large_factorial(100)) + + +if __name__ == "__main__": + main() diff --git a/solutions/0020.FactorialDigitSum/readme.md b/solutions/0020.FactorialDigitSum/readme.md new file mode 100644 index 0000000..b4a15a7 --- /dev/null +++ b/solutions/0020.FactorialDigitSum/readme.md @@ -0,0 +1,4 @@ +# Factorial Digit Sum + + +自己写大数乘法,来计算阶乘,还是比现有的库计算来得慢……无奈。 diff --git a/solutions/0021.AmicableNumbers/euler_21.py b/solutions/0021.AmicableNumbers/euler_21.py new file mode 100644 index 0000000..be6506c --- /dev/null +++ b/solutions/0021.AmicableNumbers/euler_21.py @@ -0,0 +1,96 @@ +""" +Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n). +If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable numbers. + +For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55, and 110; therefore d(220) = 284. +The proper divisors of 284 are 1, 2, 4, 71, and 142; so d(284) = 220. + +Evaluate the sum of all the amicable numbers under 10000. +""" + +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:.8f} seconds") + return result + + return wrapper + + +def sieve_primes(limit): + """埃拉托斯特尼筛法生成质数列表""" + is_prime = [True] * (limit + 1) + is_prime[0:2] = [False, False] + primes = [] + for i in range(2, limit + 1): + if is_prime[i]: + primes.append(i) + for j in range(i * i, limit + 1, i): + is_prime[j] = False + return primes + + +def prime_factors_with_sieve(n, primes): + """使用预计算的质数列表进行分解""" + factors = {} + temp = n + for p in primes: + if p * p > temp: + break + while temp % p == 0: + factors[p] = factors.get(p, 0) + 1 + temp //= p + if temp > 1: + factors[temp] = factors.get(temp, 0) + 1 + return factors + + +def get_divisors_optimized(n): + """优化版本:预计算质数加速分解""" + if n < 2: + return [1] if n == 1 else [] + + # 计算需要的质数范围 + limit = int(n**0.5) + 1 + primes = sieve_primes(limit) + + # 质因数分解 + factors = prime_factors_with_sieve(n, primes) + + # 生成除数 + divisors = [1] + for p, exp in factors.items(): + powers = [p**e for e in range(exp + 1)] + divisors = [a * b for a in divisors for b in powers] + + return sorted(divisors) + + +def sum_of_divisors(n): + """计算一个数的所有真因子之和""" + return sum(get_divisors_optimized(n)) - n + + +def find_amicable_numbers(limit): + """查找小于给定上限的全部亲和数对""" + amicable_pairs = set() + for a in range(2, limit): + b = sum_of_divisors(a) + if a != b and sum_of_divisors(b) == a: + amicable_pairs.add((min(a, b), max(a, b))) + return amicable_pairs + + +@timer +def sum_of_amicable_numbers(limit): + """计算小于给定上限的全部亲和数之和""" + return sum(a + b for a, b in find_amicable_numbers(limit)) + + +if __name__ == "__main__": + print(sum_of_amicable_numbers(10000)) diff --git a/solutions/0021.AmicableNumbers/readme.md b/solutions/0021.AmicableNumbers/readme.md new file mode 100644 index 0000000..55ff81c --- /dev/null +++ b/solutions/0021.AmicableNumbers/readme.md @@ -0,0 +1,138 @@ +# 亲和数(Amicable Numbers) + +以下来自 [wolfram数学世界](https://mathworld.wolfram.com/AmicablePair.html) 。 + +----- + +An amicable pair (m,n) (also Amicable Numbers) consists of two integers m,n for which the sum of proper divisors (the divisors excluding the number itself) of one number equals the other. Amicable pairs are occasionally called [friendly pairs](https://mathworld.wolfram.com/FriendlyPair.html) (Hoffman 1998, p. 45), although this nomenclature is to be discouraged since the numbers more commonly known as friendly pairs are defined by a different, albeit related, criterion. Symbolically, amicable pairs satisfy + +$$ + s(m) = n + s(n) = m +$$ + +where + +$$ +s(n)=\sigma(n)-n +$$ + +is the [restricted divisor function](https://mathworld.wolfram.com/RestrictedDivisorFunction.html). Equivalently, an amicable pair (m,n) satisfies + +$$ +\sigma(m)=\sigma(n)=s(m)+s(n)=m+n, +$$ + +where $\sigma(n)$ is the divisor function. The smallest amicable pair is (220, 284) which has factorizations + +$$ +220 = 11·5·2^2 +284 = 71·2^2 +$$ + +giving restricted divisor functions + +$$ +s(220) = sum{1,2,4,5,10,11,20,22,44,55,110} = 284 +s(284) = sum{1,2,4,71,142} = 220. +$$ + +The quantity + +$$ +\sigma(m)=\sigma(n)=s(m)+s(n), +$$ + +in this case, 220+284=504, is called the pair sum. The first few amicable pairs are (220, 284), (1184, 1210), (2620, 2924) (5020, 5564), (6232, 6368), (10744, 10856), (12285, 14595), (17296, 18416), (63020, 76084), ... (OEIS [A002025](http://oeis.org/A002025) and [A002046](http://oeis.org/A002046)). An exhaustive tabulation is maintained by D. Moews. + +In 1636, Fermat found the pair (17296, 18416) and in 1638, Descartes found (9363584, 9437056), although these results were actually rediscoveries of numbers known to Arab mathematicians. By 1747, Euler had found 30 pairs, a number which he later extended to 60. In 1866, 16-year old B. Nicolò I. Paganini found the small amicable pair (1184, 1210) which had eluded his more illustrious predecessors (Paganini 1866-1867; Dickson 2005, p. 47). There were 390 known amicable pairs as of 1946 (Escott 1946). There are a total of 236 amicable pairs below 10^8 (Cohen 1970), 1427 below 10^(10) (te Riele 1986), 3340 less than 10^(11) (Moews and Moews 1993ab), 4316 less than 2.01×10^(11) (Moews and Moews 1996), and 5001 less than approx 3.06×10^(11) (Moews and Moews 1996). + +Rules for producing amicable pairs include the [Thâbit ibn Kurrah rule](https://mathworld.wolfram.com/ThabitibnKurrahRule.html) rediscovered by Fermat and Descartes and extended by Euler to [Euler's rule](https://mathworld.wolfram.com/EulersRule.html). A further extension not previously noticed was discovered by Borho (1972). + +Pomerance (1981) has proved that + +$$ + [amicable numbers <=n]=1. If m=0 (mod 6) and + +$$ +n=\sigma(m)-m +$$ + +is even, then (m,n) cannot be an amicable pair (Lee 1969). The minimal and maximal values of m/n found by te Riele (1986) were + +938304290/1344480478=0.697893577... + +and + +4000783984/4001351168=0.9998582518.... + +te Riele (1986) also found 37 pairs of amicable pairs having the same pair sum. The first such pair is (609928, 686072) and (643336, 652664), which has the pair sum + +$$ +\sigma(m)=\sigma(n)=m+n=1296000. +$$ + +te Riele (1986) found no amicable n-tuples having the same pair sum for n>2. However, Moews and Moews found a triple in 1993, and te Riele found a quadruple in 1995. In November 1997, a quintuple and sextuple were discovered. The sextuple is (1953433861918, 2216492794082), (1968039941816, 2201886714184), (1981957651366, 2187969004634), (1993501042130, 2176425613870), (2046897812505, 2123028843495), (2068113162038, 2101813493962), all having pair sum 4169926656000. Amazingly, the sextuple is smaller than any known quadruple or quintuple, and is likely smaller than any quintuple. + +The earliest known odd amicable numbers all were divisible by 3. This led Bratley and McKay (1968) to conjecture that there are no amicable pairs coprime to 6 (Guy 1994, p. 56). However, Battiato and Borho (1988) found a counterexample, and now many amicable pairs are known which are not divisible by 6 (Pedersen). The smallest known example of this kind is the amicable pair (42262694537514864075544955198125, 42405817271188606697466971841875), each number of which has 32 digits. + +A search was then begun for amicable pairs coprime to 30. The first example was found by Y. Kohmoto in 1997, consisting of a pair of numbers each having 193 digits (Pedersen). Kohmoto subsequently found two other examples, and te Riele and Pedersen used two of Kohmoto's examples to calculated 243 type-(3,2) pairs coprime to 30 by means of a method which generates type-(3,2) pairs from a type-(2,1) pairs. + +No amicable pairs which are coprime to 2·3·5·7=210 are currently known. + +The following table summarizes the largest known amicable pairs discovered in recent years. The largest of these is obtained by defining + +a = 2·5·11 + +S = 37·173·409·461·2136109·2578171801921099·68340174428454377539 + +p = 925616938247297545037380170207625962997960453645121 + +q = 210958430218054117679018601985059107680988707437025081922673599999 + +q_1 = (p+q)p^(235)-1 + +q_2 = (p-S)p^(235)-1, + +then p, q, q_1 and q_2 are all primes, and the numbers + +n_1 = aSp^(235)q_1 + +n_2 = aqp^(235)q_2 + +are an amicable pair, with each member having 24073 decimal digits (Jobling 2005). + +|digits|date|reference| +|------|------|----------| +|4829|Oct. 4, 1997|M. García| +|8684|Jun. 6, 2003|Jobling and Walker 2003| +|16563|May 12, 2004|Walker et al. 2004| +|17326|May 12, 2004|Walker et al. 2004| +|24073|Mar. 10, 2005|Jobling 2005| + +Amicable pairs in Gaussian integers also exist, for example + +s(8008+3960i) = 4232-8280i + +s(4232-8280i) = 8008+3960i + +and + +s(-1105+1020i) = -2639-1228i + +s(-2639-1228i) = -1105+1020i + +(T. D. Noe, pers. comm.).