Skip to content

Commit

Permalink
Merge pull request #15 from FranzDiebold/feat/add-solution-for-proble…
Browse files Browse the repository at this point in the history
…m-53

Add solution for problem 53.
  • Loading branch information
FranzDiebold authored Jun 23, 2020
2 parents dab50ba + e075dc6 commit 00f066a
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 28 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# [Project Euler](https://projecteuler.net) Solutions

[![solved: 52 problems](https://img.shields.io/badge/solved-52_problems-f93.svg)](./src)
[![solved: 53 problems](https://img.shields.io/badge/solved-53_problems-f93.svg)](./src)
![Python: 3.8](https://img.shields.io/badge/Python-3.8-3776ab.svg)
[![Lint and Test](https://github.com/FranzDiebold/project-euler-solutions/workflows/Lint%20and%20Test/badge.svg)](https://github.com/FranzDiebold/project-euler-solutions/actions?query=workflow%3A%22Lint+and+Test%22)
[![license: MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](./LICENSE.md)
Expand Down
30 changes: 30 additions & 0 deletions src/common/calculations.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,33 @@ def calculate_large_power(base: int, exponent: int, last_n_digits_only: int = 0)
for _ in range(exponent):
power = calculate_large_product(power, base, last_n_digits_only)[-last_n_digits_only:]
return power


def calculate_large_factorial(number: int) -> str:
"""Calculate the factorial for a given number `number`.
Returns:
factorial: Factorial number as string.
"""
factorial = '1'
for i in range(1, number + 1):
# This would not be needed in Python3 any more, since there is no maximum integer.
factorial = calculate_large_product(factorial, str(i))
return factorial


# pylint: disable=invalid-name
def calculate_binomial_coefficient(n: int, k: int) -> int:
"""Calculate the binomial coefficient (n over k)."""
if n < 0:
raise ValueError('`n` must not be negative!')
if k < 0:
raise ValueError('`k` must not be negative!')
if k > n:
return 0

binomial_coefficient = 1
for i in range(k):
binomial_coefficient *= (n - i)
binomial_coefficient /= (1 + i)
return round(binomial_coefficient)
17 changes: 2 additions & 15 deletions src/p020_factorial_digit_sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,7 @@
Find the sum of the digits in the number 100!
"""

from src.common.calculations import calculate_large_product


def get_large_factorial(number: int) -> str:
"""Calculate the factorial for a given number `number`.
Returns:
factorial: Factorial number as string.
"""
factorial = '1'
for i in range(1, number + 1):
# This would not be needed in Python3 any more, since there is no maximum integer.
factorial = calculate_large_product(factorial, str(i))
return factorial
from src.common.calculations import calculate_large_factorial


def get_sum_of_digits(number: str) -> int:
Expand All @@ -34,7 +21,7 @@ def get_sum_of_digits(number: str) -> int:
def main() -> None:
"""Main function."""
factorial_number = 100
sum_of_digits = get_sum_of_digits(get_large_factorial(factorial_number))
sum_of_digits = get_sum_of_digits(calculate_large_factorial(factorial_number))
print(f'The sum of the digits in the number {factorial_number:,}! is {sum_of_digits:,}.')


Expand Down
48 changes: 48 additions & 0 deletions src/p053_combinatoric_selections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
Problem 53: Combinatoric selections
https://projecteuler.net/problem=53
There are exactly ten ways of selecting three from five, 12345:
123, 124, 125, 134, 135, 145, 234, 235, 245, and 345
In combinatorics, we use the notation, (5 over 3) = 10.
In general, (n over r) = n! / (r! * (n−r)!), where r <= n, n! = n * (n−1) * ... * 3 * 2 * 1,
and 0! = 1.
It is not until n = 23, that a value exceeds one-million: (23 over 10) = 1144066.
How many, not necessarily distinct, values of (n over r) for 1 <= n <= 100,
are greater than one-million?
"""

from typing import Iterable, Tuple

from src.common.calculations import calculate_binomial_coefficient


# pylint: disable=invalid-name
def get_large_binomial_coefficients(max_n: int, threshold: int) -> Iterable[Tuple[int, int, int]]:
"""
Get binomial coefficients (n over r) for `1 <= n <= max_n` that are greater than `threshold`.
Returns tuples `(n, r, (n over r))`.
"""
for n in range(1, max_n + 1):
for r in range(n + 1):
binomial_coefficient = calculate_binomial_coefficient(n, r)
if binomial_coefficient > threshold:
yield n, r, binomial_coefficient


def main() -> None:
"""Main function."""
max_n = 100
threshold = int(1e6)
count = len(list(get_large_binomial_coefficients(max_n, threshold)))
print(f'The number of values of (n over r) for 1 <= n <= {max_n} ' \
f'that are greater than {threshold:,} is {count}.')


if __name__ == '__main__':
main()
34 changes: 34 additions & 0 deletions test/common/test_calculations.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,37 @@ def test_calculate_large_power_with_last_n_digits_only():
# 8^9 = 134.217.728
expected_result = '17728'
assert actual_result == expected_result


@pytest.mark.parametrize('test_input_number,expected_result', [
(10, '3628800'),
(0, '1'),
])
def test_calculate_large_factorial(test_input_number, expected_result):
# arrange
from src.p020_factorial_digit_sum import calculate_large_factorial

# act
actual_result = calculate_large_factorial(test_input_number)

# assert
assert actual_result == expected_result


@pytest.mark.parametrize('test_input_n,test_input_k,expected_result', [
(5, 3, 10),
(23, 10, 1144066),
(52, 5, 2598960),
(100, 0, 1),
(100, 100, 1),
(100, 99, 100),
])
def test_calculate_binomial_coefficient(test_input_n, test_input_k, expected_result):
# arrange
from src.common.calculations import calculate_binomial_coefficient

# act
actual_result = calculate_binomial_coefficient(test_input_n, test_input_k)

# assert
assert actual_result == expected_result
12 changes: 0 additions & 12 deletions test/test_p020_factorial_digit_sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,6 @@
"""


def test_get_large_factorial():
# arrange
from src.p020_factorial_digit_sum import get_large_factorial

# act
actual_result = get_large_factorial(10)

# assert
expected_result = '3628800'
assert actual_result == expected_result


def test_get_sum_of_digits():
# arrange
from src.p020_factorial_digit_sum import get_sum_of_digits
Expand Down
30 changes: 30 additions & 0 deletions test/test_p053_combinatoric_selections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
Problem 53: Combinatoric selections
https://projecteuler.net/problem=53
There are exactly ten ways of selecting three from five, 12345:
123, 124, 125, 134, 135, 145, 234, 235, 245, and 345
In combinatorics, we use the notation, (5 over 3) = 10.
In general, (n over r) = n! / (r! * (n−r)!), where r <= n, n! = n * (n−1) * ... * 3 * 2 * 1,
and 0! = 1.
It is not until n = 23, that a value exceeds one-million: (23 over 10) = 1144066.
How many, not necessarily distinct, values of (n over r) for 1 <= n <= 100,
are greater than one-million?
"""


def test_get_large_binomial_coefficients():
# arrange
from src.p053_combinatoric_selections import get_large_binomial_coefficients

# act
actual_result_iter = get_large_binomial_coefficients(100, int(1e6))

# assert
expected_result = (23, 10, 1144066)
assert next(actual_result_iter) == expected_result

0 comments on commit 00f066a

Please sign in to comment.