From 5354d2a1815e6382c50ed9cca883c01585d0c8dc Mon Sep 17 00:00:00 2001 From: Franz Diebold Date: Thu, 3 Oct 2019 09:47:18 +0200 Subject: [PATCH] Add solution for problem 21. --- README.md | 2 +- src/p021_amicable_numbers.py | 62 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/p021_amicable_numbers.py diff --git a/README.md b/README.md index c36adbb..8d63b2b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # [Project Euler](https://projecteuler.net) Solutions -[![solved: 20 problems](https://img.shields.io/badge/solved-20_problems-f93.svg)](./src) +[![solved: 21 problems](https://img.shields.io/badge/solved-21_problems-f93.svg)](./src) ![Python: 3.7](https://img.shields.io/badge/Python-3.7-3776ab.svg) [![Build Status](https://travis-ci.com/FranzDiebold/project-euler-solutions.svg?branch=master)](https://travis-ci.com/FranzDiebold/project-euler-solutions) [![license: MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](./LICENSE.md) diff --git a/src/p021_amicable_numbers.py b/src/p021_amicable_numbers.py new file mode 100644 index 0000000..a81fb10 --- /dev/null +++ b/src/p021_amicable_numbers.py @@ -0,0 +1,62 @@ +""" +Problem 21: Amicable numbers +https://projecteuler.net/problem=21 + +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. +""" + +from typing import Iterable, Tuple +import math + + +def divisors(number: int) -> Iterable[int]: + """Get proper divisors for given number `number`. + + Example: + 220 -> [1, 2, 4, 5, 10, 11, 20, 22, 44, 55, 110] + The numbers may be in a random order. + """ + yield 1 + for i in range(2, math.floor(math.sqrt(number))): + if number % i == 0: + yield i + yield number // i + + +def get_sum_of_divisors(number: int) -> int: + """Get sum of proper divisors of number `number`.""" + return sum(divisors(number)) + + +def get_amicable_pairs(threshold: int) -> Iterable[Tuple[int, int]]: + """Get amicable number pairs (as tuples) up to a threshold.""" + number_to_sum_divisors = {} + for i in range(1, threshold): + sum_of_divisors = get_sum_of_divisors(i) + if number_to_sum_divisors.get(sum_of_divisors) == i: + yield (sum_of_divisors, i) + number_to_sum_divisors[i] = sum_of_divisors + + +def main() -> None: + """Main function.""" + threshold = 10000 + sum_of_amicable_numbers = 0 + # pylint: disable=invalid-name + for (a, b) in get_amicable_pairs(threshold): + sum_of_amicable_numbers += a + b + print(f'The sum of all the amicable numbers under {threshold:,} ' \ + f'is {sum_of_amicable_numbers:,}.') + + +if __name__ == '__main__': + main()