Tìm tổng các số Amicable-pair nhỏ hơn 100000
Problem PE021-Amicable-numbers
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.
Đề bài
d(n) được định nghĩa là tổng của các ước số của số n (không kể số n)
Nếu d(a) = b và d(b) = a và a != b –> a và b được gọi là một cặp amicable-pair. Mỗi số a, b được gọi là amicable-number.
Với ví dụ: 220 có các ước số: 1, 2, 4, 5, 10, 11, 20, 22, 44, 55, 110
d(220) = 284.
Các ước số 284 là 1, 2, 4, 71, 142.
d(284) = 220.
–> 220 và 284 được gọi là cặp amicable-pair.
Tính tổng các số amicable nhỏ hơn 10000.
Phân tích đề bài:
Bài toán này gồm các phần riêng rẽ:
- Tính ra các ước số của 1 số number.
- Tính ra tổng các ước số d(number).
- Tính ra các ước số của tổng d(d(number))
- Nếu d(number) == d(d(number)) –> number & d(number)
- Tính tổng các số number, d(number) lại.
Với phần tìm các ước số, dưới đây là một cách làm khá hay:
def get_divisor_numbers(number):
sqrt_of_number = int(math.sqrt(number))
list_divisor = [1]
if number == sqrt_of_number * sqrt_of_number:
list_divisor.append(sqrt_of_number)
sqrt_of_number -= 1
for index in range(2, sqrt_of_number):
if number % index == 0:
list_divisor.append(number)
list_divisor.append(number // index)
return list_divisor
Nếu làm cách “trâu bò” sẽ mất khá nhiều thời gian, tôi đưa ra một cách … nhanh hơn một chút đó là đặt “cache”.
“Cache” gồm có:
- Cache cho phần tính tổng các ước số.
- Cache cho phần danh sách các số amicable
import math
LIMIT_NUMBER = 100000
sum_divisor_dict = {}
def get_sum_divisor_numbers(number):
if number in sum_divisor_dict:
return sum_divisor_dict[number]
sqrt_of_number = int(math.sqrt(number))
sum_total = 1
if number == sqrt_of_number * sqrt_of_number:
sum_total += sqrt_of_number
sqrt_of_number -= 1
for index in range(2, sqrt_of_number):
if number % index == 0:
sum_total += index + number // index
sum_divisor_dict[number] = sum_total
return sum_total
def is_amicable_pairs(number):
sum_number = get_sum_divisor_numbers(number)
if number == get_sum_divisor_numbers(sum_number) and number != sum_number:
return True, number, sum_number
else:
return False, 0, 0
def get_sum_amicable_pair_numbers():
list_amicable_pairs = []
for number in range(5, LIMIT_NUMBER + 1):
if number not in list_amicable_pairs:
result = is_amicable_pairs(number)
if result[0]:
list_amicable_pairs.append(result[1])
list_amicable_pairs.append(result[2])
return sum(list_amicable_pairs)
if __name__ == "__main__":
import time
start = time.time()
result = get_sum_amicable_pair_numbers()
done = time.time()
print("The largest sum of all amicable pairs less than {}: {}".format(LIMIT_NUMBER, result))
elapsed = done - start
print("elapsed time: {}s".format(elapsed))
Thời gian chạy bài toán
The largest sum of all amicable pairs less than 100000: 852810
elapsed time: 1.5143499374389648s
Chỉ hơn 1s cho bài toán này. Cũng không tệ lắm nhỉ :D
Source-code: PE-021