Skip to content

Commit

Permalink
Add solution for problem 49.
Browse files Browse the repository at this point in the history
  • Loading branch information
FranzDiebold committed Jun 20, 2020
1 parent 959c19b commit 8935852
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 2 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: 48 problems](https://img.shields.io/badge/solved-48_problems-f93.svg)](./src)
[![solved: 49 problems](https://img.shields.io/badge/solved-49_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
17 changes: 16 additions & 1 deletion src/common/permutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
"""
# pylint: disable=invalid-name

from typing import Iterable, List, TypeVar
from typing import Iterable, List, TypeVar, Dict, Union
import copy
from collections import defaultdict

from src.common.calculations import calculate_product

Expand Down Expand Up @@ -38,3 +39,17 @@ def get_permutations(sorted_items: List[T]) -> Iterable[List[T]]:
"""Get all permutations of a given list of sorted items `sorted_items`."""
for n in range(1, get_number_of_permutations(sorted_items) + 1):
yield get_nth_lexicographic_permutation(n, sorted_items)


def _get_characters_map(content: str) -> Dict[str, int]:
"""Get the characters of a given string `content` as a map,
i.e. `abbccc` -> `{ 'a': 1, 'b': 2, 'c': 3 }`."""
characters_map = defaultdict(int)
for character in content:
characters_map[character] += 1
return characters_map


def is_permutation(a: Union[int, str], b: Union[int, str]) -> bool:
"""Check if `a` is a permutation of `b`."""
return _get_characters_map(str(a)) == _get_characters_map(str(b))
64 changes: 64 additions & 0 deletions src/p049_prime_permutations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
Problem 49: Prime permutations
https://projecteuler.net/problem=49
The arithmetic sequence, 1487, 4817, 8147, in which each of the terms increases by 3330,
is unusual in two ways: (i) each of the three terms are prime, and,
(ii) each of the 4-digit numbers are permutations of one another.
There are no arithmetic sequences made up of three 1-, 2-, or 3-digit primes,
exhibiting this property, but there is one other 4-digit increasing sequence.
What 12-digit number do you form by concatenating the three terms in this sequence?
"""
# pylint: disable=invalid-name

from typing import List, Iterable, Tuple

from src.common.primes import is_prime
from src.common.permutations import is_permutation


def get_sorted_n_digit_primes(n: int) -> List[int]:
"""Get all primes with `n` digits as a sorted list."""
n_digit_primes = []
for i in range(pow(10, n - 1), pow(10, n)):
if is_prime(i):
n_digit_primes.append(i)
return n_digit_primes


def get_n_digit_prime_arithmetic_sequences(n: int) -> Iterable[Tuple[int]]:
"""
Get arithmetic sequences made of three increasing numbers which have the following properties:
(i) each of the three terms are prime, and,
(ii) each of the 4-digit numbers are permutations of one another, and,
(iii) they are equidistant.
"""
n_digit_primes_list = get_sorted_n_digit_primes(n)
n_digit_primes_set = set(n_digit_primes_list)
num_n_digit_primes = len(n_digit_primes_list)
for idx1 in range(num_n_digit_primes):
prime_1 = n_digit_primes_list[idx1]
for idx2 in range(idx1 + 1, num_n_digit_primes):
prime_2 = n_digit_primes_list[idx2]
prime_3 = prime_2 + (prime_2 - prime_1)
if prime_3 in n_digit_primes_set and \
is_permutation(prime_1, prime_2) and \
is_permutation(prime_2, prime_3):
yield (prime_1, prime_2, prime_3)


def main() -> None:
"""Main function."""
n = 4
arithmetic_sequences = get_n_digit_prime_arithmetic_sequences(n)
next(arithmetic_sequences)
relevant_arithmetic_sequence = next(arithmetic_sequences)
concatenated_number = ''.join([str(number) for number in relevant_arithmetic_sequence])
print(f'The arithmetic sequence is {relevant_arithmetic_sequence} ' \
f'and the corresponding 12-digit number is {concatenated_number}.')


if __name__ == '__main__':
main()
35 changes: 35 additions & 0 deletions test/common/test_permutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,38 @@ def test_get_permutations():
[2, 1, 0],
]
assert list(actual_result) == expected_result


@pytest.mark.parametrize('test_input_content,expected_result', [
('', {}),
('a', {'a': 1}),
('abbccc', {'a': 1, 'b': 2, 'c': 3}),
])
def test_get_characters_map(test_input_content, expected_result):
# arrange
from src.common.permutations import _get_characters_map

# act
actual_result = _get_characters_map(test_input_content)

# assert
assert actual_result == expected_result


@pytest.mark.parametrize('test_input_a,test_input_b,expected_result', [
('', '', True),
('a', 'a', True),
('a', 'b', False),
('abc', 'acb', True),
('abc', 'abcc', False),
(12345, 24351, True),
])
def test_is_permutation(test_input_a, test_input_b, expected_result):
# arrange
from src.common.permutations import is_permutation

# act
actual_result = is_permutation(test_input_a, test_input_b)

# assert
assert actual_result == expected_result
58 changes: 58 additions & 0 deletions test/test_p049_prime_permutations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
Problem 49: Prime permutations
https://projecteuler.net/problem=49
The arithmetic sequence, 1487, 4817, 8147, in which each of the terms increases by 3330,
is unusual in two ways: (i) each of the three terms are prime, and,
(ii) each of the 4-digit numbers are permutations of one another.
There are no arithmetic sequences made up of three 1-, 2-, or 3-digit primes,
exhibiting this property, but there is one other 4-digit increasing sequence.
What 12-digit number do you form by concatenating the three terms in this sequence?
"""

import pytest


@pytest.mark.parametrize('test_input_n,expected_result', [
(1, [2, 3, 5, 7]),
(2, [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97])
])
def test_get_n_digit_primes(test_input_n, expected_result):
# arrange
from src.p049_prime_permutations import get_sorted_n_digit_primes

# act
actual_result = get_sorted_n_digit_primes(test_input_n)

# assert
assert actual_result == expected_result


@pytest.mark.parametrize('test_input_n,expected_result', [
(1, []),
(2, []),
(3, []),
])
def test_get_n_digit_prime_arithmetic_sequences_no_sequences(test_input_n, expected_result):
# arrange
from src.p049_prime_permutations import get_n_digit_prime_arithmetic_sequences

# act
actual_result_iter = get_n_digit_prime_arithmetic_sequences(test_input_n)

# assert
assert list(actual_result_iter) == expected_result


def test_get_n_digit_prime_arithmetic_sequences_first_4_digit_sequence():
# arrange
from src.p049_prime_permutations import get_n_digit_prime_arithmetic_sequences

# act
actual_result_iter = get_n_digit_prime_arithmetic_sequences(4)

# assert
expected_result = (1487, 4817, 8147)
assert next(actual_result_iter) == expected_result

0 comments on commit 8935852

Please sign in to comment.