|
| 1 | +""" |
| 2 | +Project Euler Problem 122: https://projecteuler.net/problem=122 |
| 3 | +
|
| 4 | +Efficient Exponentiation |
| 5 | +
|
| 6 | +The most naive way of computing n^15 requires fourteen multiplications: |
| 7 | +
|
| 8 | + n x n x ... x n = n^15. |
| 9 | +
|
| 10 | +But using a "binary" method you can compute it in six multiplications: |
| 11 | +
|
| 12 | + n x n = n^2 |
| 13 | + n^2 x n^2 = n^4 |
| 14 | + n^4 x n^4 = n^8 |
| 15 | + n^8 x n^4 = n^12 |
| 16 | + n^12 x n^2 = n^14 |
| 17 | + n^14 x n = n^15 |
| 18 | +
|
| 19 | +However it is yet possible to compute it in only five multiplications: |
| 20 | +
|
| 21 | + n x n = n^2 |
| 22 | + n^2 x n = n^3 |
| 23 | + n^3 x n^3 = n^6 |
| 24 | + n^6 x n^6 = n^12 |
| 25 | + n^12 x n^3 = n^15 |
| 26 | +
|
| 27 | +We shall define m(k) to be the minimum number of multiplications to compute n^k; |
| 28 | +for example m(15) = 5. |
| 29 | +
|
| 30 | +Find sum_{k = 1}^200 m(k). |
| 31 | +
|
| 32 | +It uses the fact that for rather small n, applicable for this problem, the solution |
| 33 | +for each number can be formed by increasing the largest element. |
| 34 | +
|
| 35 | +References: |
| 36 | +- https://en.wikipedia.org/wiki/Addition_chain |
| 37 | +""" |
| 38 | + |
| 39 | + |
| 40 | +def solve(nums: list[int], goal: int, depth: int) -> bool: |
| 41 | + """ |
| 42 | + Checks if nums can have a sum equal to goal, given that length of nums does |
| 43 | + not exceed depth. |
| 44 | +
|
| 45 | + >>> solve([1], 2, 2) |
| 46 | + True |
| 47 | + >>> solve([1], 2, 0) |
| 48 | + False |
| 49 | + """ |
| 50 | + if len(nums) > depth: |
| 51 | + return False |
| 52 | + for el in nums: |
| 53 | + if el + nums[-1] == goal: |
| 54 | + return True |
| 55 | + nums.append(el + nums[-1]) |
| 56 | + if solve(nums=nums, goal=goal, depth=depth): |
| 57 | + return True |
| 58 | + del nums[-1] |
| 59 | + return False |
| 60 | + |
| 61 | + |
| 62 | +def solution(n: int = 200) -> int: |
| 63 | + """ |
| 64 | + Calculates sum of smallest number of multiplactions for each number up to |
| 65 | + and including n. |
| 66 | +
|
| 67 | + >>> solution(1) |
| 68 | + 0 |
| 69 | + >>> solution(2) |
| 70 | + 1 |
| 71 | + >>> solution(14) |
| 72 | + 45 |
| 73 | + >>> solution(15) |
| 74 | + 50 |
| 75 | + """ |
| 76 | + total = 0 |
| 77 | + for i in range(2, n + 1): |
| 78 | + max_length = 0 |
| 79 | + while True: |
| 80 | + nums = [1] |
| 81 | + max_length += 1 |
| 82 | + if solve(nums=nums, goal=i, depth=max_length): |
| 83 | + break |
| 84 | + total += max_length |
| 85 | + return total |
| 86 | + |
| 87 | + |
| 88 | +if __name__ == "__main__": |
| 89 | + print(f"{solution() = }") |
0 commit comments