У меня есть N прямоугольников одинакового размера rectWidth*rectHeight. У меня есть область с размерами areaWidth*areaHeight. Я хочу разместить N прямоугольников в области, сохраняя соотношение сторон прямоугольников, изменяя размер прямоугольников, чтобы они подходили. Между прямоугольниками я хочу пробел.
Каковы должны быть размеры прямоугольников, чтобы все они поместились внутри прямоугольника и сохраняли соотношение сторон?
2 ответа
Пусть есть N прямоугольников.
Пусть прямоугольники имеют размер (cw, ch), где 0 < c ≤ 1.
Пусть область, в которую вы хотите вписаться, имеет размер (W, H).
Пусть s ≥ 0 — расстояние между прямоугольниками.
Горизонтальный размер a > 0 прямоугольников, сложенных горизонтально, равен acw + (a - 1)s.
Мы знаем, что acw + (a - 1)s ≤ W.
Вертикальный размер b > 0 прямоугольников, сложенных вертикально, равен bch + (b - 1)s.
Мы знаем, что bch + (b - 1)s ≤ H.
Тогда мы имеем следующую задачу оптимизации.
Макс. с
при условии
a ≤ (W + s) / (cw + s)
б ≤ (Н + с) / (кан + с)
аб ≥ N
0 < с ≤ 1
a, b > 0 и целое число
Теперь рассмотрим следующие неравенства.
A ≤ (W + s) / (cw + s)
б ≤ (Н + с) / (ч + с)
Любое оптимальное решение должно превращать хотя бы одно из этих неравенств в строгое неравенство.
То есть для оптимального решения (a, b, c) выполняется по крайней мере одно из следующих утверждений.
A = (W + s) / (cw + s) ↔ c = (W - s(a - 1)) / wa
b = (H + s) / (ch + s) ↔ c = (H - s (b - 1)) / wb
Предположим без ограничения общности, что выполняется a = (W + s) / (cw + s).
Поскольку a должен принимать одно из значений в {1, 2, ..., N},
c должен принимать одно из значений {W / w, (W - s) / 2w, (W - 2s) / 3w, ..., (W - (N - 1)s) / Nw}.
Аналогичные рассуждения дают список значений c, которые должны быть получены в случае, когда второе неравенство (для b) является точным.
Если вы объедините эти два списка значений, у вас будет не более 2N возможных значений, которые c может принять в оптимальном решении. Отсортируйте эти значения, затем выполните двоичный поиск максимального c в этом списке, для которого допустимы значения a и b.
Способ проверить допустимость значения c состоит в том, чтобы установить
A = пол((W + s) / (cw + s))
b = пол ((H + s) / (ch + s))
А затем проверить, что ab ≥ N.
c <= 1
и тот факт, что вы проверяете выполнимость ранее (т.е. в первом цикле). Вы оба предполагаете, что одно направление будет упаковано (без свободного места, кроме «желобов»), перебираете все количество возможных прямоугольников в каждом направлении, берете максимально возможное значение, а затем берете максимальный результат в обоих направлениях. Независимо от того, выполняете ли вы два цикла O (n) и один цикл O (2n), как Тимоти, или два цикла O (n) с вдвое большим количеством инструкций, как вы, это строго одно и то же.
Как насчет этого решения JavaScript?
var areaHeight = window.innerHeight; //set here your area height
var areaWidth = window.innerWidth; //set here your area width
var N = 216; //set amount of rectangles you want to fit
var rectRatio = 9/4; //set rectangle ratio
var gutter = [5, 10]; //set x and y spacing between rectangles
var cols, rows, rectHeight, rectWidth; //variables that we need to calculate
Функция предполагает, что сетка прямоугольников (холст) всегда будет соответствовать высоте области контейнера. Вы передаете количество строк функции, и она вычисляет размер прямоугольника и определяет, больше ли ширина холста ширины контейнера или нет. Если холст больше, мы делаем row++ и снова вызываем функцию.
function rowIterator(iterator) {
rows = iterator;
cols = Math.ceil(N/rows);
rectHeight = (areaHeight - (rows-1)*gutter[1])/rows;
rectWidth = rectHeight*rectRatio;
if (cols * rectWidth + (cols - 1)*gutter[0] > areaWidth) {
rowIterator(rows + 1);
}
}
rowIterator(1); //feed initial value
var size1 = [rectWidth, rectHeight];
Если вы также заботитесь о том, чтобы найти MAX размер прямоугольника, а не только соответствовать ему, тогда итерация должна быть выполнена также для столбцов, и следует выбрать больший размер прямоугольника:
function colIterator(iterator) {
cols = iterator;
rows = Math.ceil(N/cols);
rectWidth = (areaWidth - (cols - 1)*gutter[0])/cols;
rectHeight = rectWidth/rectRatio;
if (rows * rectHeight + (rows - 1)*gutter[1] > areaHeight) {
colIterator(cols + 1);
}
}
colIterator(1);
var size2 = [rectWidth, rectHeight];
Общее количество итераций для обоих итераторов будет около N, максимальный размер прямоугольника:
optimalRectSize = [Math.max(size1[0], size2[0]), Math.max(size1[1], size2[1])]
Похожие вопросы
Новые вопросы
algorithm
Алгоритм - это последовательность четко определенных шагов, которые определяют абстрактное решение проблемы. Используйте этот тег, если ваша проблема связана с дизайном алгоритма.