¿Qué algoritmo se puede usar para empaquetar rectángulos de diferentes tamaños en el rectángulo más pequeño posible de una manera bastante óptima?


273

Tengo un montón de objetos rectangulares que necesito empacar en el espacio más pequeño posible (las dimensiones de este espacio deben ser potencias de dos).

Soy consciente de varios algoritmos de empaque que empaquetarán los artículos lo mejor posible en un espacio determinado, sin embargo, en este caso necesito el algoritmo para determinar qué tan grande debe ser ese espacio también.

Por ejemplo, digamos que tengo los siguientes rectángulos

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

Se pueden empacar en un espacio de 128 * 128

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

Sin embargo, si también hubiera un 160 * 32 y un 64 * 64, necesitaría un espacio de 256 * 128

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

¿Qué algoritmos hay que puedan empaquetar un montón de rectángulos y determinar el tamaño requerido para el contenedor (a una potencia de 2 y dentro de un tamaño máximo dado para cada dimensión)?


66
¿No es la segunda solución no óptima? ¿No debería ser 128 por 224?
Mantas Vidutis

"las dimensiones de este espacio deben ser potencias de dos". Por lo tanto, no hay diferencia, porque lo que era / es porque no puedo suponer que la no potencia de dos es soportada incondicionalmente por el hardware subyacente.
Fire Lancer

2
De todos modos, simplificó el algoritmo al final (intente ajustarlo todo en 32x32, si no, intente 64x32, luego 64x64, 128x64, etc.) :)
Fire Lancer


Puse un tipo de solución de fuerza bruta aquí stackoverflow.com/a/47698424/1641247
Sean

Respuestas:


67

La solución rápida y sucia de primer paso siempre es excelente para comenzar, en comparación, por lo menos.

Colocación codiciosa de grande a pequeña.

Coloque el rectángulo más grande que quede en su área llena. Si no cabe en ningún lugar, colóquelo en un lugar que extienda la región del paquete lo menos posible. Repita hasta que termine con el rectángulo más pequeño.

No es perfecto en absoluto, pero es fácil y una buena línea de base. Todavía empacaría perfectamente su ejemplo original y también le daría una respuesta equivalente para el segundo.


1
Sólo estaba jugando con algo así en un trozo de papel, ahora se ve bastante óptima en la mayoría de los casos, incluso sin la rotación de los rectángulos o nada
Fuego Lancer

1
Lo implementé y ejecuté un montón de datos de prueba a través de él, parece hacer un buen trabajo solo dejando un poco de datos desperdiciados. Ahora solo necesito reescribir mi implementación para que sea más eficiente que una búsqueda lineal para cada rect a través del espacio disponible, verificando que cada píxel esté bloqueado (contra todos los rectos existentes) ...
Fire Lancer

44
Se ofrece una solución óptima en jair.org/media/3735/live-3735-6794-jair.pdf
Jim Balter

2
Me costó mucho imaginarme cuán óptimo podría funcionar esto. Así que lo he codificado (con una forma cuadrada) y los resultados son excelentes. Aquí hay una animación de demostración: imgur.com/ISjxuOR
Attila Tanyi

@JimBalter espacio cuadrado sabio ... probablemente ... en términos de velocidad y escalabilidad? ¿Realmente no?
Arek Bal

86

Consulte esta página en el proyecto ARC para obtener una encuesta de soluciones, existe una compensación entre la complejidad / tiempo de implementación y la optimización, pero hay una amplia gama de algoritmos para elegir.

Aquí hay un extracto de los algoritmos:

  1. Algoritmo de altura decreciente de primer ajuste (FFDH)
    FFDH empaqueta el siguiente elemento R (en altura no creciente) en el primer nivel donde R encaja. Si ningún nivel puede acomodar R, se crea un nuevo nivel.
    Complejidad temporal de FFDH: O (n · log n).
    Relación de aproximación: FFDH (I) <= (17/10) · OPT (I) +1; El límite asintótico de 17/10 es estrecho.

  2. Algoritmo de altura decreciente de siguiente ajuste (NFDH)
    NFDH empaqueta el siguiente elemento R (en altura no creciente) en el nivel actual si R encaja. De lo contrario, el nivel actual está "cerrado" y se crea un nuevo nivel.
    Complejidad del tiempo: O (n · log n).
    Relación de aproximación: NFDH (I) <= 2 · OPT (I) +1; El límite asintótico de 2 es estrecho.

  3. Algoritmo de altura decreciente de mejor ajuste (BFDH)
    BFDH empaqueta el siguiente elemento R (en altura no creciente) en el nivel, entre aquellos que pueden acomodar R, para los cuales el espacio horizontal residual es el mínimo. Si ningún nivel puede acomodar a R, se crea un nuevo nivel.

  4. Algoritmo Inferior Izquierdo (BL)
    Artículos de primer orden BL por ancho no creciente. BL empaqueta el siguiente artículo tan cerca de la parte inferior como quepa y luego tan cerca de la izquierda como sea posible sin superponerse con ningún artículo empacado. Tenga en cuenta que BL no es un algoritmo de empaque orientado al nivel.
    Complejidad del tiempo: O (n ^ 2).
    Relación de aproximación: BL (I) <= 3 · OPT (I).

  5. Algoritmo de Baker Up-Down (UD)
    UD utiliza una combinación de BL y una generalización de NFDH. El ancho de la tira y los elementos se normalizan de modo que la tira sea del ancho de la unidad. UD ordena los artículos en un ancho que no aumenta y luego los divide en cinco grupos, cada uno con un ancho en el rango (1/2, 1], (1 / 3,1 / 2], (1 / 4,1 / 3 ], (1 / 5,1 / 4], (0,1 / 5]. La tira también se divide en cinco regiones R1, ···, R5. Básicamente, algunos elementos de ancho en el rango (1 / i + 1, 1 / i], para 1 <= i <= 4, se empaquetan en la región Ri por BL. Dado que BL deja un espacio de ancho creciente de arriba a abajo en el lado derecho de la tira, UD aprovecha esta ventaja primero empacar el artículo en Rj para j = 1, ···, 4 (en orden) de arriba a abajo. Si no hay tal espacio, el artículo se empaca en Ri por BL. Finalmente, los artículos de tamaño máximo 1/5 se empaquetan en los espacios en R1, ···, R4 mediante el algoritmo NFDH (generalizado).
    Relación de aproximación: UD (I) <= (5/4) · OPT (I) + (53/8) H, donde H es la altura máxima de los elementos; El límite asintótico de 5/4 es apretado.

  6. Algoritmo de ajuste inverso (RF)
    RF también normaliza el ancho de la tira y los elementos para que la tira sea del ancho de la unidad. RF apila primero todos los artículos de ancho mayor que 1/2. Los artículos restantes se clasifican en una altura que no aumenta y se empaquetarán por encima de la altura H0 alcanzada por los mayores de 1/2. Entonces RF repite el siguiente proceso. Hablando en términos generales, RF empaqueta elementos de izquierda a derecha con su parte inferior a lo largo de la línea de altura H0 hasta que no haya más espacio. Luego, empaqueta los artículos de derecha a izquierda y de arriba a abajo (llamado nivel inverso) hasta que el ancho total sea al menos 1/2. Luego, se baja el nivel inverso hasta que (al menos) uno de ellos toque algún elemento a continuación. El menú desplegable se repite de alguna manera.
    Relación de aproximación: RF (I) <= 2 · OPT (I).

  7. Algoritmo de Steinberg El algoritmo de
    Steinberg, denotado como M en el documento, estima un límite superior de la altura H requerida para empacar todos los elementos de manera que se pruebe que los elementos de entrada se pueden empaquetar en un rectángulo de ancho W y altura H. Luego defina siete procedimientos (con siete condiciones), cada uno para dividir un problema en dos más pequeños y resolverlos de forma recursiva. Se ha demostrado que cualquier problema tratable satisface una de las siete condiciones.
    Relación de aproximación: M (I) <= 2 · OPT (I).

  8. Algoritmo de ajuste dividido (SF) SF divide los elementos en dos grupos, L1 con un ancho mayor que 1/2 y L2 como máximo 1/2. Todos los artículos de L1 son embalados primero por FFDH. Luego se organizan de modo que todos los elementos con un ancho superior a 2/3 estén por debajo de aquellos con un ancho máximo de 2/3. Esto crea un rectángulo R de espacio con un ancho de 1/3. Los elementos restantes en L2 se empaquetan en R y el espacio por encima de los empaquetados con L1 usando FFDH. Los niveles creados en R se consideran inferiores a los creados sobre el empaque de L1.
    Relación de aproximación: SF (I) <= (3/2) · OPT (I) + 2; El límite asintótico de 3/2 es estrecho.

  9. Algoritmo de Sleator El algoritmo de
    Sleater consta de cuatro pasos:

    1. Todos los artículos de ancho mayor que 1/2 se empaquetan uno encima del otro en la parte inferior de la tira. Suponga que h0 es la altura del empaque resultante Todos los empaques posteriores ocurrirán por encima de h0.

    2. Los artículos restantes se ordenan por altura no creciente. Se empaqueta un nivel de artículos (en orden de altura no creciente) de izquierda a derecha a lo largo de la línea de altura h0.

    3. Luego se dibuja una línea vertical en el medio para cortar la tira en dos mitades iguales (tenga en cuenta que esta línea puede cortar un artículo que está parcialmente empaquetado en la mitad derecha). Dibuje dos segmentos de línea horizontal de longitud la mitad, uno a través de la mitad izquierda (llamada línea de base izquierda) y uno a través de la mitad derecha (llamada línea de base derecha) lo más bajo posible, de modo que las dos líneas no crucen ningún elemento.

    4. Elija la línea de base izquierda o derecha que tenga una altura más baja y empaque un nivel de elementos en la mitad correspondiente de la tira hasta que el siguiente elemento sea demasiado ancho.

    Se forma una nueva línea de base y el Paso (4) se repite en la línea de base inferior hasta que todos los artículos estén empacados.
    Complejidad del tiempo: O (n · log n).
    La relación de aproximación del algoritmo de Sleator es 2.5, que es ajustada.


66
Todo esto requiere conocer el ancho del espacio.
Quantum7

1
@ Quantum7 posiblemente no sea demasiado importante ya que OP requiere que los lados sean potencias de dos, por lo que podríamos probar un montón de dimensiones con suficiente área.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

19

Echa un vistazo a los problemas de embalaje . Creo que el tuyo cae dentro del 'embalaje de contenedores 2D'. Debería poder aprender mucho de las soluciones a ese y otros problemas de empaque.

Ver también: Empaquetar datos de imágenes rectangulares en una textura cuadrada.


Aquí hay otro buen ejemplo de un algoritmo de optimización de empaquetamiento de rectángulos: codeproject.com/Articles/210979/…
Anderson Green

Problema también mencionado en: en.wikipedia.org/wiki/… Notablemente, esto restringe el embalaje del contenedor a un único contenedor de tamaño desconocido, me pregunto si todavía está NP-completo allí.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

17

Existe una extensa literatura sobre este problema. Una buena heurística codiciosa es colocar rectángulos del área más grande al más pequeño en la primera posición disponible hacia la parte inferior e izquierda del contenedor. Piense en la gravedad tirando todos los elementos hacia la esquina inferior izquierda. Para una descripción de este google "Chazelle inferior izquierda embalaje".

Para obtener soluciones óptimas, las técnicas de vanguardia pueden empacar más de 20 rectángulos en unos pocos segundos. Huang tiene un algoritmo que separa el problema de encontrar el cuadro delimitador delimitador más pequeño del problema de decidir si un conjunto de rectángulos puede caber en un cuadro delimitador de un tamaño específico. Usted le da a su programa un conjunto de rectángulos, y le indica el cuadro delimitador envolvente más pequeño requerido para empacarlos.

Para su caso, su bucle externo debe iterar desde el cuadro delimitador más pequeño posible hacia arriba (con el ancho y la altura aumentando sucesivamente por potencias de dos). Para cada uno de estos cuadros delimitadores, pruebe para ver si puede encontrar un embalaje para sus rectángulos. Obtendrá un montón de respuestas "no", hasta la primera respuesta "sí", que seguramente será la solución óptima.

Para el bucle interno de su algoritmo, el que responde "sí" o "no" a un cuadro delimitador de tamaño específico, buscaría la referencia de Huang y simplemente implementaría su algoritmo. Incluye muchas optimizaciones además del algoritmo básico, pero solo necesitas la carne y las papas básicas. Como desea manejar las rotaciones, en cada punto de ramificación durante su búsqueda, simplemente intente ambas rotaciones y retroceda cuando ambas rotaciones no den como resultado una solución.


9

Estoy bastante seguro de que este es un problema NP-difícil , por lo que, para una solución óptima, tendría que implementar un algoritmo de retroceso que intente todas las combinaciones posibles.

La buena noticia es que, debido a la necesidad de empacar rectángulos 2D en un espacio 2D limitado, puede podar muchas posibilidades desde el principio, por lo que podría no ser TAN malo.


3
Probablemente quieras decir NP-completo.
starblue el

77
Bueno, si es NP completo, entonces es fácil de resolver, solo resuelve una instancia equivalente del vendedor ambulante, y listo. Pero es trivial mostrar que, tal como se plantea, no lo es, ya que los problemas de NP completo son problemas de decisión (obtiene respuestas sí / no) y tiene un algoritmo de verificación de tiempo polinómico. La pregunta "¿existe una disposición de rectángulos a, b, c ... que ocupan un área menor que 256 * 128 podría ser NP-completo.
Eclipse

2
@Eclipse es correcto. De jair.org/media/3735/live-3735-6794-jair.pdf "El problema de optimización es NP-hard, mientras que el problema de decidir si se puede empaquetar un conjunto de rectángulos en un cuadro delimitador dado es NP-complete, a través de una reducción del embalaje de contenedores (Korf, 2003) ". Sin embargo, tenga en cuenta que el OP solicitó "una forma bastante óptima", y hay soluciones para eso en P, para definiciones suficientemente amplias de "justamente".
Jim Balter

También sospecho de la dureza NP, pero necesitamos una referencia / prueba.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

2
Holy thread necro, Batman. Este es un problema de empaque, y ya se ha demostrado que es NP-hard en el mejor de los casos: en.wikipedia.org/wiki/Packing_problems
Blindy

2

Lo que necesita está en https://github.com/nothings/stb/blob/master/stb_rect_pack.h

muestra:

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);
}

1

Una solución general es no trivial (las matemáticas hablan por completo de una mierda imposible).
En general, las personas usan un algoritmo genético para probar posibles combinaciones, pero puedes hacerlo razonablemente bien al poner primero la forma más grande y luego probar diferentes lugares para el siguiente más grande y así sucesivamente.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.