perf: Project Euler problem 145 solution 1 (#6259)

Improve solution (~30 times - from 900+ seconds to ~30 seconds)

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
This commit is contained in:
Maxim Smolskiy 2022-07-23 04:53:46 +03:00 committed by GitHub
parent b3d9281591
commit c45fb3c294
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 26 deletions

View File

@ -444,6 +444,7 @@
* [Scoring Functions](machine_learning/scoring_functions.py) * [Scoring Functions](machine_learning/scoring_functions.py)
* [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py) * [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py)
* [Similarity Search](machine_learning/similarity_search.py) * [Similarity Search](machine_learning/similarity_search.py)
* [Support Vector Machines](machine_learning/support_vector_machines.py)
* [Word Frequency Functions](machine_learning/word_frequency_functions.py) * [Word Frequency Functions](machine_learning/word_frequency_functions.py)
## Maths ## Maths
@ -500,6 +501,7 @@
* [Gaussian](maths/gaussian.py) * [Gaussian](maths/gaussian.py)
* [Greatest Common Divisor](maths/greatest_common_divisor.py) * [Greatest Common Divisor](maths/greatest_common_divisor.py)
* [Greedy Coin Change](maths/greedy_coin_change.py) * [Greedy Coin Change](maths/greedy_coin_change.py)
* [Hamming Numbers](maths/hamming_numbers.py)
* [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py) * [Hardy Ramanujanalgo](maths/hardy_ramanujanalgo.py)
* [Integration By Simpson Approx](maths/integration_by_simpson_approx.py) * [Integration By Simpson Approx](maths/integration_by_simpson_approx.py)
* [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py) * [Is Ip V4 Address Valid](maths/is_ip_v4_address_valid.py)
@ -938,6 +940,7 @@
* [Bogo Sort](sorts/bogo_sort.py) * [Bogo Sort](sorts/bogo_sort.py)
* [Bubble Sort](sorts/bubble_sort.py) * [Bubble Sort](sorts/bubble_sort.py)
* [Bucket Sort](sorts/bucket_sort.py) * [Bucket Sort](sorts/bucket_sort.py)
* [Circle Sort](sorts/circle_sort.py)
* [Cocktail Shaker Sort](sorts/cocktail_shaker_sort.py) * [Cocktail Shaker Sort](sorts/cocktail_shaker_sort.py)
* [Comb Sort](sorts/comb_sort.py) * [Comb Sort](sorts/comb_sort.py)
* [Counting Sort](sorts/counting_sort.py) * [Counting Sort](sorts/counting_sort.py)

View File

@ -1,6 +1,6 @@
""" """
Project Euler problem 145: https://projecteuler.net/problem=145 Project Euler problem 145: https://projecteuler.net/problem=145
Author: Vineet Rao Author: Vineet Rao, Maxim Smolskiy
Problem statement: Problem statement:
Some positive integers n have the property that the sum [ n + reverse(n) ] Some positive integers n have the property that the sum [ n + reverse(n) ]
@ -13,44 +13,82 @@ There are 120 reversible numbers below one-thousand.
How many reversible numbers are there below one-billion (10^9)? How many reversible numbers are there below one-billion (10^9)?
""" """
EVEN_DIGITS = [0, 2, 4, 6, 8]
ODD_DIGITS = [1, 3, 5, 7, 9]
def odd_digits(num: int) -> bool: def reversible_numbers(
remaining_length: int, remainder: int, digits: list[int], length: int
) -> int:
""" """
Check if the number passed as argument has only odd digits. Count the number of reversible numbers of given length.
>>> odd_digits(123) Iterate over possible digits considering parity of current sum remainder.
False >>> reversible_numbers(1, 0, [0], 1)
>>> odd_digits(135797531) 0
True >>> reversible_numbers(2, 0, [0] * 2, 2)
20
>>> reversible_numbers(3, 0, [0] * 3, 3)
100
""" """
while num > 0: if remaining_length == 0:
digit = num % 10 if digits[0] == 0 or digits[-1] == 0:
if digit % 2 == 0: return 0
return False
num //= 10 for i in range(length // 2 - 1, -1, -1):
return True remainder += digits[i] + digits[length - i - 1]
if remainder % 2 == 0:
return 0
remainder //= 10
return 1
if remaining_length == 1:
if remainder % 2 == 0:
return 0
result = 0
for digit in range(10):
digits[length // 2] = digit
result += reversible_numbers(
0, (remainder + 2 * digit) // 10, digits, length
)
return result
result = 0
for digit1 in range(10):
digits[(length + remaining_length) // 2 - 1] = digit1
if (remainder + digit1) % 2 == 0:
other_parity_digits = ODD_DIGITS
else:
other_parity_digits = EVEN_DIGITS
for digit2 in other_parity_digits:
digits[(length - remaining_length) // 2] = digit2
result += reversible_numbers(
remaining_length - 2,
(remainder + digit1 + digit2) // 10,
digits,
length,
)
return result
def solution(max_num: int = 1_000_000_000) -> int: def solution(max_power: int = 9) -> int:
""" """
To evaluate the solution, use solution() To evaluate the solution, use solution()
>>> solution(1000) >>> solution(3)
120 120
>>> solution(1_000_000) >>> solution(6)
18720 18720
>>> solution(10_000_000) >>> solution(7)
68720 68720
""" """
result = 0 result = 0
# All single digit numbers reverse to themselves, so their sums are even for length in range(1, max_power + 1):
# Therefore at least one digit in their sum is even result += reversible_numbers(length, 0, [0] * length, length)
# Last digit cannot be 0, else it causes leading zeros in reverse
for num in range(11, max_num):
if num % 10 == 0:
continue
num_sum = num + int(str(num)[::-1])
num_is_reversible = odd_digits(num_sum)
result += 1 if num_is_reversible else 0
return result return result