Какой алгоритм можно использовать для упаковки прямоугольников разного размера в самый маленький прямоугольник, который может быть достаточно оптимальным?

У меня есть куча прямоугольных объектов, которые мне нужно упаковать в наименьшее возможное пространство (размеры этого пространства должны быть равны двум).

Я знаю различные алгоритмы упаковки, которые будут упаковывать элементы, а также, возможно, в заданное пространство, однако в этом случае мне нужен алгоритм для определения того, насколько большим будет это пространство.

Скажем, Ive получил следующие прямоугольники

  • 128 * 32
  • 128 * 64
  • 64 * 32
  • 64 * 32

Они могут быть упакованы в пространство 128 * 128

 _________________
|128*32          |
|________________|
|128*64          |
|                |
|                |
|________________|
|64*32  |64*32   |
|_______|________|

Однако, если бы имелось также 160 * 32 и 64 * 64, ему понадобилось бы пространство размером 256 * 128

 ________________________________
|128*32          |64*64  |64*32  |
|________________|       |_______|
|128*64          |       |64*32  |
|                |_______|_______|
|                |               |
|________________|___            |
|160*32              |           |
|____________________|___________|

Какие существуют алгоритмы, которые могут упаковать кучу прямоугольников и определить требуемый размер для контейнера (до степени 2 и в пределах заданного максимального размера для каждого измерения)?

Ответ 1

Быстрое и грязное решение первого прохода всегда отличное, как сравнение, если ничего другого.

Жадное размещение от большого до маленького.

Поместите самый большой прямоугольник, оставшийся в вашей упакованной области. Если он не может уместиться нигде, поместите его в место, которое как можно меньше расширяет область упаковки. Повторяйте, пока не закончите с самым маленьким прямоугольником.

Это не идеально, но это легко и приятно. Он все равно полностью упакует ваш оригинальный пример и даст вам эквивалентный ответ для второго.

Ответ 2

См. эту страницу в проекте ARC для обзора решений, существует компромисс между сложностью реализации/временем и оптимальностью, но существует широкий спектр алгоритмов на выбор.

Здесь выдержка из алгоритмов:

  • Алгоритм с уменьшенной высотой (FFDH) - FFDH упаковывает следующий элемент R (в не увеличивающейся высоте) на первом уровне, где R подходит. Если ни один уровень не может вместить R, создается новый уровень.
    Временная сложность FFDH: O (n · log n).
    Отношение аппроксимации: FFDH (I) <= (17/10) · OPT (I) +1; асимптотическая граница 17/10 плотна.

  • Алгоритм Next-Fit Decreasing Height (NFDH)
    NFDH упаковывает следующий элемент R (в возрастающей высоте) на текущий уровень, если R подходит. В противном случае текущий уровень "закрыт" и создается новый уровень.
    Сложность времени: O (n · log n).
    Отношение приближения: NFDH (I) <= 2 · OPT (I) +1; асимптотическая оценка 2 плотна.

  • Лучший алгоритм снижения высоты (BFDH)
    BFDH упаковывает следующий элемент R (в возрастающей высоте) на уровне, среди тех, которые могут вместить R, для которых минимальное остаточное горизонтальное пространство. Если ни один уровень не может вместить R, создается новый уровень.

  • Алгоритм Bottom-Left (BL)
    BL предметов первого заказа по не увеличивающейся ширине. BL упаковывает следующий элемент как можно ближе к дну, так как он поместится, а затем как можно ближе к левому, так как он может идти без наложения на любой упакованный предмет. Обратите внимание, что BL не является алгоритмом упаковки, ориентированным на уровень.
    Сложность времени: O (n ^ 2).
    Отношение приближения: BL (I) <= 3 · OPT (I).

  • Алгоритм Baker Up-Down (UD)
    UD использует комбинацию BL и обобщение NFDH. Ширина полосы и элементов нормализована так, что полоса имеет ширину единицы. UD упорядочивает элементы в не увеличивающейся ширине, а затем делит элементы на пять групп, каждая с шириной в диапазоне (1/2, 1], (1/3,1/2], (1/4,1/3 ], (1/5,1/4], (0,1/5]. Полоса также разделена на пять областей R1,..., R5. В основном некоторые элементы ширины в диапазоне (1/i + 1, 1/i], для 1 <= я <= 4, упакованы в область Ri через BL. Так как BL оставляет пространство с увеличением ширины сверху вниз на правой стороне полосы, UD использует это преимущество сначала упаковывая элемент в Rj для j = 1,..., 4 (по порядку) сверху вниз. Если такого пространства нет, элемент упаковывается в Ri через BL. Наконец, элементы размером не более 1/5 упаковываются в пространства в R1,..., R4 с помощью (обобщенного) алгоритма NFDH. Опять же, если в этих областях нет места, элемент упаковывается в R5 с использованием NFDH.
    Отношение приближения: UD (I) <= (5/4) · OPT (I) + (53/8) H, где H - максимальная высота элементов; асимптотическая оценка 5/4 плотна.

  • Алгоритм обратного соответствия (RF)
    RF также нормализует ширину полосы и элементов, чтобы полоса имела ширину единицы. RF сначала складывает все элементы шириной больше 1/2. Оставшиеся предметы сортируются в не увеличивающейся высоте и будут упаковываться выше высоты H0, достигнутой теми, которые превышают 1/2. Затем RF повторяет следующий процесс. Грубо говоря, RF кладет предметы слева направо со своим дном вдоль линии высоты H0, пока не будет больше места. Затем упаковывает элементы справа налево и сверху вниз (называется обратным уровнем), пока общая ширина не будет равна 1/2. Затем обратный уровень сбрасывается до тех пор, пока (по крайней мере) один из них не коснется некоторого пункта ниже. Выпадение как-то повторяется.
    Отношение приближения: RF (I) <= 2 · OPT (I).

  • Алгоритм Стейнберга
    Алгоритм Штейнберга, обозначенный как M в статье, оценивает верхнюю границу высоты H, необходимую для упаковки всех элементов, так что доказано, что входные элементы могут быть упакованы в прямоугольник ширины W и высоты H. Затем они определяют семь процедуры (с семью условиями), каждая из которых делит проблему на две более мелкие и рекурсивно решает. Было показано, что любая приемлемая задача удовлетворяет одному из семи условий. Отношение аппроксимации: M (I) <= 2 OPT (I).

  • Алгоритм Split-Fit (SF) SF делит предметы на две группы, L1 с шириной больше 1/2 и L2 не более 1/2. Все элементы L1 сначала упаковываются FFDH. Затем они расположены так, чтобы все предметы шириной более 2/3 были ниже ширины не более 2/3. Это создает прямоугольник R пространства шириной 1/3. Оставшиеся предметы в L2 затем упаковываются в R и пространство над теми, которые упакованы L1, используя FFDH. Уровни, созданные в R, считаются ниже уровней, созданных над упаковкой L1.
    Отношение приближения: SF (I) <= (3/2) · OPT (I) + 2; асимптотическая оценка 3/2 плотна.

  • Алгоритм сглаживания
    Алгоритм сглаживания состоит из четырех этапов:

    • Все элементы шириной больше 1/2 упаковываются друг над другом в нижней части полосы. Предположим, что h0 - высота полученной упаковки. Вся последующая упаковка будет происходить выше h0.

    • Оставшиеся предметы упорядочены по возрастающей высоте. Уровень предметов упаковывается (в порядке возрастания высоты) слева направо вдоль линии высоты h0.

    • Затем вертикальная линия рисуется посередине, чтобы разрезать полоску на две равные половины (обратите внимание, что эта строка может разрезать элемент, который частично упакован в правую половину). Нарисуйте два горизонтальных отрезка длины по одну половину, одну по левой половине (называемую левой базой), а одну по правой половине (называемую правой базой) как можно ниже, чтобы две линии не пересекали какой-либо элемент.

    • Выберите левую или правую базовую линию с меньшей высотой и упакуйте уровень предметов в соответствующую половину полосы до тех пор, пока следующий элемент не станет слишком большим.

    Создается новая базовая линия, и шаг (4) повторяется на нижней базовой линии до тех пор, пока все предметы не будут упакованы.
    Сложность времени: O (n · log n).
    Коэффициент аппроксимации алгоритма сглаживания 2.5, который является плотным.

Ответ 4

Существует обширная литература по этой проблеме. Хорошая жадная эвристика заключается в размещении прямоугольников от наибольшей площади до самой маленькой в ​​первом доступном положении к нижней и левой части контейнера. Подумайте о гравитации, потянув все предметы до нижнего левого угла. Для описания этого google "Chazelle bottom left packing".

Для оптимальных решений самые современные методы могут упаковать более 20 прямоугольников за несколько секунд. У Хуанга есть алгоритм который отделяет проблему поиска наименьшего охватывающего ограничивающего прямоугольника от проблемы определения того, может ли набор прямоугольников вписываться в ограничивающий прямоугольник определенного размера. Вы даете его программе набор прямоугольников, и он сообщает вам самую маленькую ограничительную рамку, необходимую для их упаковки.

Для вашего случая ваш внешний цикл должен переходить от минимально возможного ограничивающего прямоугольника вверх (с шириной и высотой, последовательно увеличивающейся по степеням двух). Для каждого из этих ограничивающих полей проверьте, можете ли вы найти упаковку для ваших прямоугольников. Вы получите кучу "нет" ответов до первого ответа "да", который будет гарантированно оптимальным решением.

Для внутреннего цикла вашего алгоритма - тот, который отвечает "да" или "нет" на ограничивающий прямоугольник определенного размера, я бы посмотрел ссылку Хуан и просто выполнил его алгоритм. Он включает в себя множество оптимизаций поверх основного алгоритма, но вам действительно нужно только основное мясо и картофель. Поскольку вы хотите обрабатывать вращения, в каждой точке ветвления во время поиска, просто попробуйте оба вращения и откат, когда оба вращения не приводят к решению.

Ответ 5

Я вполне уверен, что это сложная задача для NP, поэтому для оптимального решения вам придется реализовать алгоритм обратного отслеживания, который пробует каждую возможную комбинацию.

Хорошая новость заключается в том, что из-за необходимости упаковать двухмерные прямоугольники в ограниченное двухмерное пространство вы можете сократить множество возможностей на ранних этапах, так что это может быть не так уж плохо.

Ответ 6

Что вам нужно в https://github.com/nothings/stb/blob/master/stb_rect_pack.h

Пример:

stbrp_context context;

struct stbrp_rect rects[100];

for (int i=0; i< 100; i++)
{
    rects[i].id = i;
    rects[i].w = 100+i;
    rects[i].h = 100+i;
    rects[i].x = 0;
    rects[i].y = 0;
    rects[i].was_packed = 0;
}

int rectsLength = sizeof(rects)/sizeof(rects[0]);

int nodeCount = 4096*2;
struct stbrp_node nodes[nodeCount];


stbrp_init_target(&context, 4096, 4096, nodes, nodeCount);
stbrp_pack_rects(&context, rects, rectsLength);

for (int i=0; i< 100; i++)
{
    printf("rect %i (%hu,%hu) was_packed=%i\n", rects[i].id, rects[i].x, rects[i].y, rects[i].was_packed);
}

Ответ 7

Общее решение нетривиально (математика говорит, что это невозможно)
Как правило, люди используют генетический алгоритм для попыток возможных комбинаций, но вы можете сделать это достаточно хорошо, если сначала поместить самую большую фигуру, а затем попробовать разные места для следующего по величине и т.д.

Ответ 8

Я использую один из:

https://codereview.stackexchange.com/info/179565/incremental-2d-rectangle-bin-packer?newreg=cce6c6101cf349c58423058762fa12b2

Он реализует гильотинный алгоритм и требует в качестве входных данных одно измерение и пытается оптимизировать другое (вы также можете установить максимум с небольшим изменением в коде). Возможно, если вы попробуете разные значения двух значений, это сработает для вас.

Он ни в коем случае не оптимален, но он небольшой, переносимый (только .h) и не имеет никакой другой зависимости, кроме C++ и STL.