-
-
Notifications
You must be signed in to change notification settings - Fork 51k
Problem 061 Project Euler #14798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Problem 061 Project Euler #14798
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,346 @@ | ||
| """ | ||
| Project Euler Problem 061: https://projecteuler.net/problem=61 | ||
|
|
||
| Problem statement - | ||
| Triangle, square, pentagonal, hexagonal, heptagonal, and octagonal numbers are all | ||
| figurate (polygonal) numbers and are generated by the following formulae: | ||
|
|
||
| Triangle P(3,n) = n(n+1)/2 1,3,6,10,15,... | ||
| Square P(4,n) = n^2 1,4,9,16,25,... | ||
| Pentagonal P(5,n) = n(3n-1)/2 1,5,12,22,35,... | ||
| Hexagonal P(6,n) = n(2n-1) 1,6,15,28,45,... | ||
| Heptagonal P(7,n) = n(5n-3)/2 1,7,18,34,55,... | ||
| Octagonal P(8,n) = n(3n-2) 1,8,21,40,65,... | ||
|
|
||
| The ordered set of three 4-digit numbers: 8128, 2882, 8281, | ||
| has three interesting properties. | ||
|
|
||
| 1. The set is cyclic, in that the last two digits of each number is the first two | ||
| digits of the next number (including the last number with the first). | ||
| 2. Each polygonal type: triangle(P(3,127)), square(P(4,91)), and pentagonal(P(5,44)), | ||
| is represented by a different number in the set. | ||
| 3. This is the only set of 4-digit numbers with this property. | ||
|
|
||
| Find the sum of the only ordered set of six cyclic -digit numbers for which each | ||
| polygonal type: triangle, square, pentagonal, hexagonal, heptagonal, and octagonal, | ||
| is represented by a different number in the set. | ||
|
|
||
|
|
||
| Solution explanation- | ||
| The solution is actually pretty simple using a brute force approach. | ||
| We first do some precomputations to get all the relevant sets of polygon numbers. | ||
| Individual helper functions have been made for the same. | ||
| We also have one more helper function to check if two given numbers are cyclic or not. | ||
|
|
||
| Now the approach is to pick one number from a set then try to find a number in another | ||
| set that is cyclic to the previous. Now consider this new number as previous and find | ||
| a new number in another set(set that has not been used yet).Continue doing this until | ||
| you either reach your last set, in which case you need only check for cyclic against | ||
| the first number, if it matches you have an answer. If you cannot find a cyclic number | ||
| for previous in your current set, you backtrack to the prev set and chose the next | ||
| number from it. | ||
|
|
||
| So code wise, we generate all possiblee permutations of the sets using itertools built | ||
| in permutations method. Then its justa bunch of for loops trying to find the answer. | ||
| Once we have a valid ordered set, we return its sum. | ||
|
|
||
| """ | ||
|
|
||
| import itertools | ||
|
|
||
|
|
||
| def fill_triangle(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: | ||
| """ | ||
| Populates the triangle array with relevant integers i.e greater than | ||
| range[0] and less than range[1]+1. | ||
| The range provided should be inclusive. | ||
|
|
||
| >>> fill_triangle((1,20)) | ||
| [1, 3, 6, 10, 15] | ||
|
|
||
| """ | ||
|
|
||
| def triangle(n: int) -> int: | ||
| return n * (n + 1) // 2 | ||
|
|
||
| st, end = int_range | ||
| arr = [] | ||
| i = 1 | ||
| while True: | ||
| k = triangle(i) | ||
| if k < st: | ||
| i += 1 | ||
| continue | ||
| if k > end: | ||
| break | ||
| arr.append(k) | ||
| i += 1 | ||
| return arr | ||
|
|
||
|
|
||
| def fill_square(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: | ||
| """ | ||
| Populates the square array with relevant integers i.e greater than | ||
| range[0] and less than range[1]+1. | ||
| The range provided should be inclusive. | ||
|
|
||
| >>> fill_square((1,30)) | ||
| [1, 4, 9, 16, 25] | ||
|
|
||
| """ | ||
|
|
||
| def square(n: int) -> int: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file Please provide descriptive name for the parameter: |
||
| return n**2 | ||
|
|
||
| st, end = int_range | ||
| arr = [] | ||
| i = 1 | ||
| while True: | ||
| k = square(i) | ||
| if k < st: | ||
| i += 1 | ||
| continue | ||
| if k > end: | ||
| break | ||
| arr.append(k) | ||
| i += 1 | ||
| return arr | ||
|
|
||
|
|
||
| def fill_pentagonal(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: | ||
| """ | ||
| Populates the pentagonal array with relevant integers i.e greater than | ||
| range[0] and less than range[1]+1. | ||
| The range provided should be inclusive. | ||
|
|
||
| >>> fill_pentagonal((1,40)) | ||
| [1, 5, 12, 22, 35] | ||
|
|
||
| """ | ||
|
|
||
| def pentagon(n: int) -> int: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file Please provide descriptive name for the parameter: |
||
| return n * (3 * n - 1) // 2 | ||
|
|
||
| st, end = int_range | ||
| arr = [] | ||
| i = 1 | ||
| while True: | ||
| k = pentagon(i) | ||
| if k < st: | ||
| i += 1 | ||
| continue | ||
| if k > end: | ||
| break | ||
| arr.append(k) | ||
| i += 1 | ||
| return arr | ||
|
|
||
|
|
||
| def fill_hexagonal(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: | ||
| """ | ||
| Populates the hexagonal array with relevant integers i.e greater than | ||
| range[0] and less than range[1]+1. | ||
| The range provided should be inclusive. | ||
|
|
||
| >>> fill_hexagonal((1,50)) | ||
| [1, 6, 15, 28, 45] | ||
|
|
||
| """ | ||
|
|
||
| def hexagon(n: int) -> int: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file Please provide descriptive name for the parameter: |
||
| return n * (2 * n - 1) | ||
|
|
||
| st, end = int_range | ||
| arr = [] | ||
| i = 1 | ||
| while True: | ||
| k = hexagon(i) | ||
| if k < st: | ||
| i += 1 | ||
| continue | ||
| if k > end: | ||
| break | ||
| arr.append(k) | ||
| i += 1 | ||
| return arr | ||
|
|
||
|
|
||
| def fill_heptagonal(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: | ||
| """ | ||
| Populates the heptagonal array with relevant integers i.e greater than | ||
| range[0] and less than range[1]+1. | ||
| The range provided should be inclusive. | ||
|
|
||
| >>> fill_heptagonal((1,60)) | ||
| [1, 7, 18, 34, 55] | ||
|
|
||
| """ | ||
|
|
||
| def heptagon(n: int) -> int: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file Please provide descriptive name for the parameter: |
||
| return n * (5 * n - 3) // 2 | ||
|
|
||
| st, end = int_range | ||
| arr = [] | ||
| i = 1 | ||
| while True: | ||
| k = heptagon(i) | ||
| if k < st: | ||
| i += 1 | ||
| continue | ||
| if k > end: | ||
| break | ||
| arr.append(k) | ||
| i += 1 | ||
| return arr | ||
|
|
||
|
|
||
| def fill_octagonal(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: | ||
| """ | ||
| Populates the octagonal array with relevant integers i.e greater than | ||
| range[0] and less than range[1]+1. | ||
| The range provided should be inclusive. | ||
|
|
||
| >>> fill_octagonal((1,70)) | ||
| [1, 8, 21, 40, 65] | ||
|
|
||
|
|
||
| """ | ||
|
|
||
| def octagon(n: int) -> int: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file Please provide descriptive name for the parameter: |
||
| return n * (3 * n - 2) | ||
|
|
||
| st, end = int_range | ||
| arr = [] | ||
| i = 1 | ||
| while True: | ||
| k = octagon(i) | ||
| if k < st: | ||
| i += 1 | ||
| continue | ||
| if k > end: | ||
| break | ||
| arr.append(k) | ||
| i += 1 | ||
| return arr | ||
|
|
||
|
|
||
| def check_cyclic(x: int, y: int) -> bool: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please provide descriptive name for the parameter: Please provide descriptive name for the parameter: |
||
| """ | ||
| This function checks if two 4 digit numbers are cyclic. | ||
| For this problem we are only concerned with 4 digit numbers. | ||
|
|
||
| Raises: | ||
| ValueError | ||
|
|
||
|
|
||
| >>> check_cyclic(8228, 2810) | ||
| True | ||
| >>> check_cyclic(8228, 2410) | ||
| False | ||
|
|
||
| """ | ||
|
|
||
| if x < 1000 or y < 1000: | ||
| raise ValueError("Both integers must be greater than 999") | ||
| if x > 9999 or y > 9999: | ||
| raise ValueError("Both integers must be less than 10000") | ||
|
|
||
| return (x % 100) == (y // 100) | ||
|
|
||
|
|
||
| def solution() -> int: | ||
| """ | ||
| The function gives a solution to problem 061 of project Euler. | ||
| The function does some precomputations first to get all the | ||
| relevant sets of polygon numbers then applies a for loop on | ||
| all possible permutations of the sets and check for a valid | ||
| answer. | ||
|
|
||
| >>> solution() | ||
| 28684 | ||
|
|
||
| """ | ||
|
|
||
| # Make initial arrays to store all types of numbers in individual arrays. | ||
| # This will make accessing them easier in the future. | ||
| # We will be fill each of these arrays only considering numbers greater | ||
| # than 999 and less than 10000. | ||
| # Since we will add them in a systematic manner, these will be present | ||
| # in a sorted order(increasing order). | ||
| triangle = fill_triangle() | ||
| square = fill_square() | ||
| pentagonal = fill_pentagonal() | ||
| hexagonal = fill_hexagonal() | ||
| heptagonal = fill_heptagonal() | ||
| octagonal = fill_octagonal() | ||
|
|
||
| # create a dictionary of all polygons for access later | ||
| polygon_dict = { | ||
| 0: triangle, | ||
| 1: square, | ||
| 2: pentagonal, | ||
| 3: hexagonal, | ||
| 4: heptagonal, | ||
| 5: octagonal, | ||
| } | ||
|
|
||
| answer = [] | ||
|
|
||
| # The elements can be in any order from any of the sets, | ||
| # so we need to consider all possible permutations of the sets. | ||
| permutations = list(itertools.permutations(range(6))) | ||
|
|
||
| # Now we simply apply a for loop to consider all permutations and | ||
| # check if that permutation leads to a valid answer. | ||
| for perm in permutations: | ||
| first_set = polygon_dict[perm[0]] | ||
| second_set = polygon_dict[perm[1]] | ||
| third_set = polygon_dict[perm[2]] | ||
| fourth_set = polygon_dict[perm[3]] | ||
| fifth_set = polygon_dict[perm[4]] | ||
| sixth_set = polygon_dict[perm[5]] | ||
| for first in first_set: | ||
| for second in second_set: | ||
| prev = first | ||
| if not (check_cyclic(prev, second)): | ||
| continue | ||
| for third in third_set: | ||
| prev = second | ||
| if not (check_cyclic(prev, third)): | ||
| continue | ||
| for fourth in fourth_set: | ||
| prev = third | ||
| if not (check_cyclic(prev, fourth)): | ||
| continue | ||
| for fifth in fifth_set: | ||
| prev = fourth | ||
| if not (check_cyclic(prev, fifth)): | ||
| continue | ||
| for sixth in sixth_set: | ||
| prev = fifth | ||
| if not (check_cyclic(prev, sixth)): | ||
| continue | ||
| if check_cyclic(sixth, first): | ||
| answer = [ | ||
| first, | ||
| second, | ||
| third, | ||
| fourth, | ||
| fifth, | ||
| sixth, | ||
| ] | ||
| # print(answer) | ||
| return sum(answer) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| # import time | ||
|
|
||
| # start_time = time.perf_counter() | ||
|
|
||
| print(f"{solution() = }") | ||
|
|
||
| # end_time = time.perf_counter() | ||
|
|
||
| # execution_time = end_time - start_time | ||
| # print(f"Execution time: {execution_time:.6f} seconds") | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As there is no test file in this pull request nor any test function or class in the file
project_euler/problem_061/sol1.py, please provide doctest for the functiontrianglePlease provide descriptive name for the parameter:
n