From 2a4d2b7a45fc729d1bda74b372ffe2e48ebf01c1 Mon Sep 17 00:00:00 2001 From: Parth Date: Thu, 11 Jun 2026 19:56:29 +0530 Subject: [PATCH 1/3] added __init__ file --- project_euler/problem_061/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 project_euler/problem_061/__init__.py diff --git a/project_euler/problem_061/__init__.py b/project_euler/problem_061/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 From d1e48492c283e3b2296bd2f014f23e6edf97a5b4 Mon Sep 17 00:00:00 2001 From: Parth Date: Thu, 11 Jun 2026 19:57:43 +0530 Subject: [PATCH 2/3] Problem 61 project euler completed --- project_euler/problem_061/sol1.py | 346 ++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 project_euler/problem_061/sol1.py diff --git a/project_euler/problem_061/sol1.py b/project_euler/problem_061/sol1.py new file mode 100644 index 000000000000..7a2efcae5166 --- /dev/null +++ b/project_euler/problem_061/sol1.py @@ -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: + 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: + 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: + 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: + 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: + 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: + """ + 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") From e2550e90b145ab80c753f284764d89d2adc62581 Mon Sep 17 00:00:00 2001 From: Parth Date: Thu, 11 Jun 2026 20:13:24 +0530 Subject: [PATCH 3/3] fixed code as per bot comments to pass automated tests --- project_euler/problem_061/sol1.py | 74 ++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/project_euler/problem_061/sol1.py b/project_euler/problem_061/sol1.py index 7a2efcae5166..be5428797d78 100644 --- a/project_euler/problem_061/sol1.py +++ b/project_euler/problem_061/sol1.py @@ -60,8 +60,15 @@ def fill_triangle(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: """ - def triangle(n: int) -> int: - return n * (n + 1) // 2 + def triangle(num: int) -> int: + """ + Gives the Nth triangle number. + >>> triangle(2) + 3 + >>> triangle(5) + 15 + """ + return num * (num + 1) // 2 st, end = int_range arr = [] @@ -89,8 +96,15 @@ def fill_square(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: """ - def square(n: int) -> int: - return n**2 + def square(num: int) -> int: + """ + Gives the Nth square number. + >>> square(2) + 4 + >>> square(5) + 25 + """ + return num**2 st, end = int_range arr = [] @@ -118,8 +132,15 @@ def fill_pentagonal(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: """ - def pentagon(n: int) -> int: - return n * (3 * n - 1) // 2 + def pentagon(num: int) -> int: + """ + Gives the Nth pentagon number. + >>> pentagon(2) + 5 + >>> pentagon(5) + 35 + """ + return num * (3 * num - 1) // 2 st, end = int_range arr = [] @@ -147,8 +168,15 @@ def fill_hexagonal(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: """ - def hexagon(n: int) -> int: - return n * (2 * n - 1) + def hexagon(num: int) -> int: + """ + Gives the Nth hexagon number. + >>> hexagon(2) + 6 + >>> hexagon(5) + 45 + """ + return num * (2 * num - 1) st, end = int_range arr = [] @@ -176,8 +204,15 @@ def fill_heptagonal(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: """ - def heptagon(n: int) -> int: - return n * (5 * n - 3) // 2 + def heptagon(num: int) -> int: + """ + Gives the Nth heptagon number. + >>> heptagon(2) + 7 + >>> heptagon(5) + 55 + """ + return num * (5 * num - 3) // 2 st, end = int_range arr = [] @@ -206,8 +241,15 @@ def fill_octagonal(int_range: tuple[int, int] = (1000, 9999)) -> list[int]: """ - def octagon(n: int) -> int: - return n * (3 * n - 2) + def octagon(num: int) -> int: + """ + Gives the Nth octagon number. + >>> octagon(2) + 8 + >>> octagon(5) + 65 + """ + return num * (3 * num - 2) st, end = int_range arr = [] @@ -224,7 +266,7 @@ def octagon(n: int) -> int: return arr -def check_cyclic(x: int, y: int) -> bool: +def check_cyclic(num1: int, num2: int) -> bool: """ This function checks if two 4 digit numbers are cyclic. For this problem we are only concerned with 4 digit numbers. @@ -240,12 +282,12 @@ def check_cyclic(x: int, y: int) -> bool: """ - if x < 1000 or y < 1000: + if num1 < 1000 or num2 < 1000: raise ValueError("Both integers must be greater than 999") - if x > 9999 or y > 9999: + if num1 > 9999 or num2 > 9999: raise ValueError("Both integers must be less than 10000") - return (x % 100) == (y // 100) + return (num1 % 100) == (num2 // 100) def solution() -> int: