patternpythonMinor
Derive integer lengths for given areas
Viewed 0 times
deriveforlengthsareasgiveninteger
Problem
I want to build a function that, given an area size (
The first two lengths in the list correspond to an orthogonal plate whose length is approximately 1.5 times larger than the one of the square plate and whose width is 1.5 times smaller. I want the lengths to be whole numbers.
The function I wrote for the task is given below, but I am sure there is a much better/faster solution. Keep in mind that when it comes to rounding floats, the bigger size for the length must be chosen since it is more conservative, but the main consideration is to not overshoot the target more than I have to.
For example, for a
How can the above be made more efficient?
target), returns a list of three lengths. The last item of the list (my_list[-1]) corresponds to the square plate. The first two lengths in the list correspond to an orthogonal plate whose length is approximately 1.5 times larger than the one of the square plate and whose width is 1.5 times smaller. I want the lengths to be whole numbers.
The function I wrote for the task is given below, but I am sure there is a much better/faster solution. Keep in mind that when it comes to rounding floats, the bigger size for the length must be chosen since it is more conservative, but the main consideration is to not overshoot the target more than I have to.
import math
def get_plates(target):
square = math.ceil(target ** 0.5)
ortho1 = [math.ceil(target ** 0.5 * 1.5), math.floor(target ** 0.5 * 1.5)]
ortho2 = [math.ceil(target ** 0.5 / 1.5), math.floor(target ** 0.5 / 1.5)]
temp = list(zip([ortho1[0] for _ in ortho2], ortho2))
temp.extend(list(zip([ortho1[1] for _ in ortho2], ortho2)))
temp = [x for x in temp if x[0]*x[1] >= target]
temp.sort(key=lambda x: abs(x[0] * x[1] - target))
return [*temp[0], square]For example, for a
target = 700 the get_plates(700) correctly returns [39, 18, 27] and not [40, 18, 27] since 39x18 = 702 whereas 40x18 = 720 and 40x17 = 680How can the above be made more efficient?
Solution
You don't need your
Also, you should use some more descriptive names.
You can use the fact that
You can also compute the square root of
You should add a
I would separate the two concerns of your function. One is to find the side length of a square most closely matching
You can use
With this, your code becomes:
temp list, you can just use itertools.product to build it:>>> list(itertools.product(ortho1, ortho2))
[(41, 18), (41, 17), (40, 18), (40, 17)]Also, you should use some more descriptive names.
You can use the fact that
tuples are also iterable and can be written like so: t = 0, 1, saving you the unneeded list brackets.You can also compute the square root of
target once and save it.You should add a
docstring describing what your function does.I would separate the two concerns of your function. One is to find the side length of a square most closely matching
target, the other one is finding a rectangle with ratio about 1:2.You can use
min and re-use your key function for that, since you only need the pair with the smallest distance and don't care about all others. min is \$\mathcal{O}(n)\$, whereas sort is usually \$\mathcal{O}(n \log n)\$. The abs is also not needed anymore, since we made sure that only those pairs with a product greater or equal the target are still left.With this, your code becomes:
import math
import itertools
def get_plates_square(target):
"""Returns the integer side length of a square with an area at least `target`."""
return math.ceil(target ** 0.5)
def get_plates(target):
"""
Return a rectangle with integer sides with a ratio of about 2:1,
which is closest to the given `target` area.
"""
target_sqrt = target ** 0.5
sides_a = math.ceil(target_sqrt * 1.5), math.floor(target_sqrt * 1.5)
sides_b = math.ceil(target_sqrt / 1.5), math.floor(target_sqrt / 1.5)
rectangles = [(a, b)
for a, b in itertools.product(sides_a, sides_b)
if a * b >= target]
return min(rectangles, key=lambda x: x[0] * x[1] - target)Code Snippets
>>> list(itertools.product(ortho1, ortho2))
[(41, 18), (41, 17), (40, 18), (40, 17)]import math
import itertools
def get_plates_square(target):
"""Returns the integer side length of a square with an area at least `target`."""
return math.ceil(target ** 0.5)
def get_plates(target):
"""
Return a rectangle with integer sides with a ratio of about 2:1,
which is closest to the given `target` area.
"""
target_sqrt = target ** 0.5
sides_a = math.ceil(target_sqrt * 1.5), math.floor(target_sqrt * 1.5)
sides_b = math.ceil(target_sqrt / 1.5), math.floor(target_sqrt / 1.5)
rectangles = [(a, b)
for a, b in itertools.product(sides_a, sides_b)
if a * b >= target]
return min(rectangles, key=lambda x: x[0] * x[1] - target)Context
StackExchange Code Review Q#154560, answer score: 4
Revisions (0)
No revisions yet.