Skip to content

Added solution for Project Euler problem 085 #4024

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

Merged
merged 4 commits into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Empty file.
102 changes: 102 additions & 0 deletions project_euler/problem_085/sol1.py
Original file line number Diff line number Diff line change
@@ -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() = }")