From d1d7a12351665efe328fa456a25431a8de7e8a29 Mon Sep 17 00:00:00 2001 From: Freddy Pringle Date: Mon, 7 Dec 2020 15:54:08 +0100 Subject: [PATCH 1/4] Added solution for Project Euler problem 085. --- project_euler/problem_085/__init__.py | 0 project_euler/problem_085/sol1.py | 102 ++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 project_euler/problem_085/__init__.py create mode 100644 project_euler/problem_085/sol1.py diff --git a/project_euler/problem_085/__init__.py b/project_euler/problem_085/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/project_euler/problem_085/sol1.py b/project_euler/problem_085/sol1.py new file mode 100644 index 000000000000..277830d87677 --- /dev/null +++ b/project_euler/problem_085/sol1.py @@ -0,0 +1,102 @@ +""" +Project Euler Problem 85: https://projecteuler.net/problem=85 + +By counting carefully it can be seen that a rectangular grid measuring 3 by 2 +contains eighteen rectangles. + +Although there exists no rectangular grid that contains exactly two million +rectangles, find the area of the grid with the nearest solution. + +Solution: + + For a grid with side-lengths a and b, the number of rectangles contained in the grid + is [a*(a+1)/2] * [b*(b+1)/2)], which happens to be the product of the a-th and b-th + triangle numbers. So to find the solution grid (a,b), we need to find the two + triangle numbers whose product is closest to two million. + + Denote these two triangle numbers Ta and Tb. We want their product Ta*Tb to be + as close as possible to 2m. Assuming that the best solution is fairly close to 2m, + We can assume that both Ta and Tb are roughly bounded by 2m. Since Ta = a(a+1)/2, + we can assume that a (and similarly b) are roughly bounded by sqrt(2 * 2m) = 2000. + Since this is a rough bound, to be on the safe side we add 10%. Therefore we start + by generating all the triangle numbers Ta for 1 <= a <= 2200. This can be done + iteratively since the ith triangle number is the sum of 1,2, ... ,i, and so + T(i) = T(i-1) + i. + + We then search this list of triangle numbers for the two that give a product + closest to our target of two million. Rather than testing every combination of 2 + elements of the list, which would find the result in quadratic time, we can find + the best pair in linear time. + + We iterate through the list of triangle numbers using enumerate() so we have a + and Ta. Since we want Ta * Tb to be as close as possible to 2m, we know that Tb + needs to be roughly 2m / Ta. Using the formula Tb = b*(b+1)/2 as well as the + quadratic formula, we can solve for b: + b is roughly (-1 + sqrt(1 + 8 * 2m / Ta)) / 2. + + Since the closest integers to this estimate will give product closest to 2m, + we only need to consider the integers above and below. It's then a simple matter + to get the triangle numbers corresponding to those integers, calculate the product + Ta * Tb, compare that product to our target 2m, and keep track of the (a,b) pair + that comes the closest. + + +Reference: https://en.wikipedia.org/wiki/Triangular_number + https://en.wikipedia.org/wiki/Quadratic_formula +""" + + +from math import ceil, floor, sqrt +from typing import List + + +def solution(target: int = 2000000) -> int: + """ + Find the area of the grid which contains as close to two million rectangles + as possible. + >>> solution(20) + 6 + >>> solution(2000) + 72 + >>> solution(2000000000) + 86595 + """ + triangle_numbers: List[int] = [0] + idx: int + + for idx in range(1, ceil(sqrt(target * 2) * 1.1)): + triangle_numbers.append(triangle_numbers[-1] + idx) + + best_product: int = 0 # we want this to be as close as possible to target + area: int = 0 # the area a*b corresponding to the grid that gives + # the product closest to target + b_estimate: float # an estimate of b, using the quadratic formula + b_floor: int # the largest integer less than b_estimate + b_ceil: int # the largest integer less than b_estimate + triangle_b_first_guess: int # the triangle number corresponding to b_floor + triangle_b_second_guess: int # the triangle number corresponding to b_ceil + + for idx_a, triangle_a in enumerate(triangle_numbers[1:], 1): + b_estimate = (-1 + sqrt(1 + 8 * target / triangle_a)) / 2 + b_floor = floor(b_estimate) + b_ceil = ceil(b_estimate) + triangle_b_first_guess = triangle_numbers[b_floor] + triangle_b_second_guess = triangle_numbers[b_ceil] + + if abs(target - triangle_b_first_guess * triangle_a) < abs( + target - best_product + ): + best_product = triangle_b_first_guess * triangle_a + area = idx_a * b_floor + + if abs(target - triangle_b_second_guess * triangle_a) < abs( + target - best_product + ): + best_product = triangle_b_second_guess * triangle_a + area = idx_a * b_ceil + + return area + + +if __name__ == "__main__": + print(f"{solution() = }") From b438aeff8f903d1ccbfbdb8a3cfd99ad62b8e09b Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Thu, 10 Dec 2020 09:39:27 +0000 Subject: [PATCH 2/4] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 10523a85c48e..cb582e793ade 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -727,6 +727,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_080/sol1.py) * Problem 081 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_081/sol1.py) + * Problem 085 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_085/sol1.py) * Problem 087 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_087/sol1.py) * Problem 089 From 0dc322451c7218b1219ad6e829c35ae28c5db000 Mon Sep 17 00:00:00 2001 From: Freddy Pringle Date: Thu, 10 Dec 2020 12:44:10 +0100 Subject: [PATCH 3/4] Minor tweaks to Project Euler problem 85 --- project_euler/problem_085/sol1.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/project_euler/problem_085/sol1.py b/project_euler/problem_085/sol1.py index 277830d87677..a850c62590be 100644 --- a/project_euler/problem_085/sol1.py +++ b/project_euler/problem_085/sol1.py @@ -54,6 +54,14 @@ def solution(target: int = 2000000) -> int: """ Find the area of the grid which contains as close to two million rectangles as possible. + Explanation of variables: + best_product: we want this to be as close as possible to target + area: the area corresponding to the grid that gives the product closest to target + b_estimate: an estimate of b, using the quadratic formula + b_floor: the largest integer less than b_estimate + b_ceil: the largest integer less than b_estimate + triangle_b_first_guess: the triangle number corresponding to b_floor + triangle_b_second_guess: the triangle number corresponding to b_ceil >>> solution(20) 6 >>> solution(2000) @@ -67,14 +75,13 @@ def solution(target: int = 2000000) -> int: for idx in range(1, ceil(sqrt(target * 2) * 1.1)): triangle_numbers.append(triangle_numbers[-1] + idx) - best_product: int = 0 # we want this to be as close as possible to target - area: int = 0 # the area a*b corresponding to the grid that gives - # the product closest to target - b_estimate: float # an estimate of b, using the quadratic formula - b_floor: int # the largest integer less than b_estimate - b_ceil: int # the largest integer less than b_estimate - triangle_b_first_guess: int # the triangle number corresponding to b_floor - triangle_b_second_guess: int # the triangle number corresponding to b_ceil + best_product: int = 0 + area: int = 0 + b_estimate: float + b_floor: int + b_ceil: int + triangle_b_first_guess: int + triangle_b_second_guess: int for idx_a, triangle_a in enumerate(triangle_numbers[1:], 1): b_estimate = (-1 + sqrt(1 + 8 * target / triangle_a)) / 2 From eacaefbfc12ac81e28737ef3fd7295c36dadac07 Mon Sep 17 00:00:00 2001 From: Freddy Pringle Date: Thu, 10 Dec 2020 13:35:48 +0100 Subject: [PATCH 4/4] Variable comments for project euler problem 85 --- project_euler/problem_085/sol1.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/project_euler/problem_085/sol1.py b/project_euler/problem_085/sol1.py index a850c62590be..74e36b1301a4 100644 --- a/project_euler/problem_085/sol1.py +++ b/project_euler/problem_085/sol1.py @@ -54,14 +54,6 @@ def solution(target: int = 2000000) -> int: """ Find the area of the grid which contains as close to two million rectangles as possible. - Explanation of variables: - best_product: we want this to be as close as possible to target - area: the area corresponding to the grid that gives the product closest to target - b_estimate: an estimate of b, using the quadratic formula - b_floor: the largest integer less than b_estimate - b_ceil: the largest integer less than b_estimate - triangle_b_first_guess: the triangle number corresponding to b_floor - triangle_b_second_guess: the triangle number corresponding to b_ceil >>> solution(20) 6 >>> solution(2000) @@ -75,12 +67,19 @@ def solution(target: int = 2000000) -> int: for idx in range(1, ceil(sqrt(target * 2) * 1.1)): triangle_numbers.append(triangle_numbers[-1] + idx) + # we want this to be as close as possible to target best_product: int = 0 + # the area corresponding to the grid that gives the product closest to target area: int = 0 + # an estimate of b, using the quadratic formula b_estimate: float + # the largest integer less than b_estimate b_floor: int + # the largest integer less than b_estimate b_ceil: int + # the triangle number corresponding to b_floor triangle_b_first_guess: int + # the triangle number corresponding to b_ceil triangle_b_second_guess: int for idx_a, triangle_a in enumerate(triangle_numbers[1:], 1):