|
1 | 1 | """
|
2 | 2 | Project Euler problem 145: https://projecteuler.net/problem=145
|
3 |
| -Author: Vineet Rao |
| 3 | +Author: Vineet Rao, Maxim Smolskiy |
4 | 4 | Problem statement:
|
5 | 5 |
|
6 | 6 | Some positive integers n have the property that the sum [ n + reverse(n) ]
|
|
13 | 13 |
|
14 | 14 | How many reversible numbers are there below one-billion (10^9)?
|
15 | 15 | """
|
| 16 | +EVEN_DIGITS = [0, 2, 4, 6, 8] |
| 17 | +ODD_DIGITS = [1, 3, 5, 7, 9] |
16 | 18 |
|
17 | 19 |
|
18 |
| -def odd_digits(num: int) -> bool: |
| 20 | +def reversible_numbers( |
| 21 | + remaining_length: int, remainder: int, digits: list[int], length: int |
| 22 | +) -> int: |
19 | 23 | """
|
20 |
| - Check if the number passed as argument has only odd digits. |
21 |
| - >>> odd_digits(123) |
22 |
| - False |
23 |
| - >>> odd_digits(135797531) |
24 |
| - True |
| 24 | + Count the number of reversible numbers of given length. |
| 25 | + Iterate over possible digits considering parity of current sum remainder. |
| 26 | + >>> reversible_numbers(1, 0, [0], 1) |
| 27 | + 0 |
| 28 | + >>> reversible_numbers(2, 0, [0] * 2, 2) |
| 29 | + 20 |
| 30 | + >>> reversible_numbers(3, 0, [0] * 3, 3) |
| 31 | + 100 |
25 | 32 | """
|
26 |
| - while num > 0: |
27 |
| - digit = num % 10 |
28 |
| - if digit % 2 == 0: |
29 |
| - return False |
30 |
| - num //= 10 |
31 |
| - return True |
| 33 | + if remaining_length == 0: |
| 34 | + if digits[0] == 0 or digits[-1] == 0: |
| 35 | + return 0 |
32 | 36 |
|
| 37 | + for i in range(length // 2 - 1, -1, -1): |
| 38 | + remainder += digits[i] + digits[length - i - 1] |
33 | 39 |
|
34 |
| -def solution(max_num: int = 1_000_000_000) -> int: |
| 40 | + if remainder % 2 == 0: |
| 41 | + return 0 |
| 42 | + |
| 43 | + remainder //= 10 |
| 44 | + |
| 45 | + return 1 |
| 46 | + |
| 47 | + if remaining_length == 1: |
| 48 | + if remainder % 2 == 0: |
| 49 | + return 0 |
| 50 | + |
| 51 | + result = 0 |
| 52 | + for digit in range(10): |
| 53 | + digits[length // 2] = digit |
| 54 | + result += reversible_numbers( |
| 55 | + 0, (remainder + 2 * digit) // 10, digits, length |
| 56 | + ) |
| 57 | + return result |
| 58 | + |
| 59 | + result = 0 |
| 60 | + for digit1 in range(10): |
| 61 | + digits[(length + remaining_length) // 2 - 1] = digit1 |
| 62 | + |
| 63 | + if (remainder + digit1) % 2 == 0: |
| 64 | + other_parity_digits = ODD_DIGITS |
| 65 | + else: |
| 66 | + other_parity_digits = EVEN_DIGITS |
| 67 | + |
| 68 | + for digit2 in other_parity_digits: |
| 69 | + digits[(length - remaining_length) // 2] = digit2 |
| 70 | + result += reversible_numbers( |
| 71 | + remaining_length - 2, |
| 72 | + (remainder + digit1 + digit2) // 10, |
| 73 | + digits, |
| 74 | + length, |
| 75 | + ) |
| 76 | + return result |
| 77 | + |
| 78 | + |
| 79 | +def solution(max_power: int = 9) -> int: |
35 | 80 | """
|
36 | 81 | To evaluate the solution, use solution()
|
37 |
| - >>> solution(1000) |
| 82 | + >>> solution(3) |
38 | 83 | 120
|
39 |
| - >>> solution(1_000_000) |
| 84 | + >>> solution(6) |
40 | 85 | 18720
|
41 |
| - >>> solution(10_000_000) |
| 86 | + >>> solution(7) |
42 | 87 | 68720
|
43 | 88 | """
|
44 | 89 | result = 0
|
45 |
| - # All single digit numbers reverse to themselves, so their sums are even |
46 |
| - # Therefore at least one digit in their sum is even |
47 |
| - # Last digit cannot be 0, else it causes leading zeros in reverse |
48 |
| - for num in range(11, max_num): |
49 |
| - if num % 10 == 0: |
50 |
| - continue |
51 |
| - num_sum = num + int(str(num)[::-1]) |
52 |
| - num_is_reversible = odd_digits(num_sum) |
53 |
| - result += 1 if num_is_reversible else 0 |
| 90 | + for length in range(1, max_power + 1): |
| 91 | + result += reversible_numbers(length, 0, [0] * length, length) |
54 | 92 | return result
|
55 | 93 |
|
56 | 94 |
|
|
0 commit comments