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

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

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

например, сказать 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 и в пределах заданного максимального размера для каждого измерение)?

7 ответов


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

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

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

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


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

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

  1. алгоритм уменьшения высоты (FFDH)
    FFDH упаковывает следующий элемент R (в не увеличивающейся высоте) на первом уровне, где r подходит. Если ни один уровень не может вместить R, новый уровень создан.
    Временная сложность FFDH: O (N * log n).
    Коэффициент аппроксимации: FFDH(I)

  2. Next-Fit алгоритм уменьшения высоты (NFDH)
    Nfdh упаковывает следующий элемент R (в не увеличивающейся высоте) на текущем уровне, если R подходит. В противном случае текущий уровень будет "закрыт" и будет создан новый уровень.
    Временная сложность: O (N * log n).
    Коэффициент аппроксимации: NFDH (I)

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

  4. нижний левый (BL) алгоритм
    Детали первого заказа BL non-увеличивая шириной. BL упаковывает следующий элемент так близко к дну, как он будет соответствовать и затем как можно ближе к левому краю, как это может идти без перекрытия с любым упакованным элементом. Обратите внимание, что BL не является алгоритмом упаковки, ориентированным на уровень.
    Временная сложность: O (n^2).
    Коэффициент приближения: BL(I)

  5. алгоритм Бейкера вверх-вниз (UD)
    UD использует комбинацию BL и обобщение NFDH. Ширина полосы и элементов нормализуется таким образом, чтобы полоса имела ширину единицы. UD приказывает детали в non-увеличивая ширине и после этого делит предметы на пять групп, каждая с шириной в диапазоне (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 Коэффициент аппроксимации: UD (I)

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

  7. алгоритм Штейнберга
    Алгоритм Штейнберга, обозначенный в статье Как M, оценивает верхнюю границу высоты H, необходимую для упаковки всех элементов, так что доказано, что входные элементы могут быть упакованы в прямоугольник ширины W и высоты H. затем они определяют семь процедуры (с семью условиями), каждая из которых делит задачу на две меньшие и решает их рекурсивно. Показано, что любая разрешимая задача удовлетворяет одному из семи условий.
    Коэффициент приближения: M(I)

  8. алгоритм Split-Fit (SF) SF делит детали в 2 группы, L1 с шириной больше чем 1/2 и L2 самое большее 1/2. Все детали L1 сперва упакованы FFDH. После этого они аранжированы так, что все детали с шириной больше чем 2/3 под теми с шириной самое большее 2/3. Это создает прямоугольник R пространства шириной 1/3. Остальные предметы в L2 затем упаковываются в R и пространство над теми, которые упакованы с L1 с помощью FFDH. Уровни, созданные в R, считаются ниже уровней, созданных над упаковкой L1.
    Коэффициент аппроксимации: SF (I)

  9. Sleator это
    Алгоритм Слейтера состоит из четырех шаги:
    1. все детали ширины больше чем 1/2 упакованы поверх Одина другого в дне прокладки. Предположим, что H0-высота результирующей упаковки, вся последующая упаковка будет происходить выше h0.

    2. остальные детали приказаны non-увеличивая высотой. Уровень предметов упаковывается (в порядке без увеличения высоты) слева направо по линии высоты h0.

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

    формируется новая базовая линия, и Шаг (4) повторяется на нижней базовой линии до тех пор, пока все элементы не будут упакованы.
    Временная сложность: O (N * log n).
    Коэффициент аппроксимации алгоритма Sleator составляет 2,5, что является плотным.


посмотреть проблемы с упаковкой. Я думаю, что ваш попадает под " 2D bin packing.- Ты сможешь многому научиться у решения этой и других проблем с упаковкой.

Смотрите также: упаковка прямоугольных данных изображения в квадратную текстуру.


существует обширная литература по этой проблеме. Хорошей жадной эвристикой является размещение прямоугольников от наибольшей площади до наименьшей в первом доступном положении снизу и слева от контейнера. Подумайте о гравитации, тянущей все предметы вниз в нижний левый угол. Для описания этого google "chazelle нижняя левая упаковка".

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

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

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


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

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


что вам нужно на 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);
}

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