Quale algoritmo può essere utilizzato per impacchettare rettangoli di dimensioni diverse nel rettangolo più piccolo possibile in modo abbastanza ottimale?


273

Ho un mucchio di oggetti rettangolari che devo mettere nello spazio più piccolo possibile (le dimensioni di questo spazio dovrebbero essere potenze di due).

Sono a conoscenza di vari algoritmi di impacchettamento che imballeranno gli articoli nel miglior modo possibile in un determinato spazio, tuttavia in questo caso ho bisogno dell'algoritmo per capire quanto deve essere grande anche quello spazio.

Ad esempio dire che ho i seguenti rettangoli

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

Possono essere impacchettati in uno spazio 128 * 128

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

Tuttavia se ci fosse anche un 160 * 32 e uno 64 * 64 avrebbe bisogno di uno spazio 256 * 128

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

Quali algoritmi esistono in grado di impacchettare un gruppo di rettangoli e determinare la dimensione richiesta per il contenitore (fino a una potenza di 2 e entro una data dimensione massima per ogni dimensione)?


6
La seconda soluzione non è ottimale? Non dovrebbe essere 128 per 224?
Mantas Vidutis,

"le dimensioni di questo spazio dovrebbero essere potenze di due" Quindi non fa differenza, per quello che era / è perché non posso supporre che la non-potenza di due sia supportata incondizionatamente dall'hardware sottostante.
Fire Lancer,

2
In ogni caso ha reso l'algoritmo più semplice alla fine (prova ad adattarlo tutto in 32x32, se non per poi provare 64x32, quindi 64x64, 128x64, ecc.) :)
Fire Lancer


Ho messo qui un tipo di soluzione di forza bruta stackoverflow.com/a/47698424/1641247
Sean,

Risposte:


67

La soluzione di primo passaggio rapida e sporca è sempre un'ottima per iniziare, come confronto se non altro.

Posizionamento goloso da grande a piccolo.

Inserisci il rettangolo più grande rimasto nella tua area affollata. Se non può andare da nessuna parte, posizionalo in un luogo che estenda la regione del pacchetto il meno possibile. Ripeti fino al termine con il rettangolo più piccolo.

Non è affatto perfetto ma è facile e una buona base. Comprenderebbe comunque perfettamente il tuo esempio originale e ti darebbe una risposta equivalente anche per il secondo.


1
Stavo solo giocando con qualcosa del genere su un po 'di carta, in questo momento sembra abbastanza ottimale nella maggior parte dei casi, anche senza ruotare i rettangoli o altro
Fire Lancer

1
L'ho implementato ed eseguito un sacco di dati di test attraverso di esso, sembra fare un buon lavoro lasciando solo un po 'di dati sprecati. Ora ho solo bisogno di riscrivere la mia implementazione per essere più efficiente di una ricerca lineare per ogni rettangolo attraverso lo spazio disponibile verificando che ogni pixel sia bloccato da quel punto (contro tutti i rect esistenti) ...
Fire Lancer

4
Una soluzione ottimale è data in jair.org/media/3735/live-3735-6794-jair.pdf
Jim Balter,

2
Ho avuto difficoltà a immaginare quanto potesse funzionare in modo ottimale. Quindi l'ho codificato (con una forma quadrata) e i risultati sono fantastici. Ecco un'animazione demo: imgur.com/ISjxuOR
Attila Tanyi,

@JimBalter spazio quadrato saggio ... probabilmente ... in termini di velocità e scalabilità? Non proprio?
Arek Bal,

86

Vedi questa pagina sul progetto ARC per un sondaggio di soluzioni, c'è un compromesso tra complessità / tempo di implementazione e ottimalità, ma c'è una vasta gamma di algoritmi tra cui scegliere.

Ecco un estratto degli algoritmi:

  1. Algoritmo di altezza decrescente di primo adattamento (FFDH)
    FFDH racchiude l'articolo R successivo (in altezza non crescente) al primo livello dove R si adatta. Se nessun livello può ospitare R, viene creato un nuovo livello.
    Complessità temporale di FFDH: O (n · log n).
    Rapporto di approssimazione: FFDH (I) <= (17/10) · OPT (I) +1; il limite asintotico del 17/10 è stretto.

  2. Algoritmo di altezza decrescente Next-Fit (NFDH)
    NFDH impacchetta l'elemento successivo R (in altezza non crescente) sul livello corrente se R si adatta. Altrimenti, il livello corrente è "chiuso" e viene creato un nuovo livello.
    Complessità temporale: O (n · log n).
    Rapporto di approssimazione: NFDH (I) <= 2 · OPT (I) +1; il limite asintotico di 2 è stretto.

  3. L'algoritmo
    BFDH (BFDH) Best Fit Fit BFDH racchiude l'elemento successivo R (in altezza non crescente) al livello, tra quelli che possono ospitare R, per i quali lo spazio orizzontale residuo è il minimo. Se nessun livello può ospitare R, viene creato un nuovo livello.

  4. Algoritmo in basso a sinistra (BL)
    BL articoli del primo ordine per larghezza non crescente. BL imballa l'articolo successivo il più vicino possibile e quindi il più vicino possibile a sinistra senza sovrapporsi con alcun articolo imballato. Si noti che BL non è un algoritmo di imballaggio orientato al livello.
    Complessità temporale: O (n ^ 2).
    Rapporto di approssimazione: BL (I) <= 3 · OPT (I).

  5. Algoritmo Baker's Up-Down (UD)
    UD utilizza una combinazione di BL e una generalizzazione di NFDH. La larghezza della striscia e gli articoli sono normalizzati in modo che la striscia sia di larghezza unitaria. UD ordina gli articoli con larghezza non crescente e quindi li divide in cinque gruppi, ciascuno con larghezza nell'intervallo (1/2, 1], (1 / 3,1 / 2], (1 / 4,1 / 3 ], (1 / 5,1 / 4], (0,1 / 5]. La striscia è anche divisa in cinque regioni R1, ···, R5. Fondamentalmente, alcuni elementi di larghezza nell'intervallo (1 / i + 1, 1 / i], per 1 <= i <= 4, sono impacchettati nella regione Ri da BL. Poiché BL lascia uno spazio di larghezza crescente dall'alto verso il basso sul lato destro della striscia, UD ne approfitta per primo imballando l'articolo in Rj per j = 1, ···, 4 (in ordine) dall'alto verso il basso. Se non c'è tale spazio, l'articolo viene imballato in Ri da BL. Infine, gli articoli di dimensioni al massimo 1/5 sono impacchettati negli spazi in R1, ···, R4 dall'algoritmo (generalizzato) NFDH.
    Rapporto di approssimazione: UD (I) <= (5/4) · OPT (I) + (53/8) H, dove H è l'altezza massima degli oggetti; il limite asintotico di 5/4 è stretto.

  6. L'algoritmo di adattamento inverso (RF)
    RF normalizza anche la larghezza della striscia e gli articoli in modo che la striscia sia della larghezza dell'unità. RF prima impila tutti gli oggetti di larghezza maggiore di 1/2. Gli oggetti rimanenti vengono ordinati in altezza non crescente e verranno imballati sopra l'altezza H0 raggiunta da quelli superiori a 1/2. Quindi RF ripete il seguente processo. In parole povere, RF confeziona gli articoli da sinistra a destra con il loro fondo lungo la linea di altezza H0 fino a quando non c'è più spazio. Quindi imballa gli articoli da destra a sinistra e dall'alto verso il basso (chiamato livello inverso) fino a quando la larghezza totale è almeno 1/2. Quindi il livello inverso viene abbassato fino a quando (almeno) uno di essi tocca qualche elemento in basso. Il menu a discesa viene in qualche modo ripetuto.
    Rapporto di approssimazione: RF (I) <= 2 · OPT (I).

  7. L'algoritmo di Steinberg L'algoritmo di
    Steinberg, indicato come M nel foglio, stima un limite superiore dell'altezza H necessaria per imballare tutti gli elementi in modo tale che sia dimostrato che gli elementi in input possono essere imballati in un rettangolo di larghezza W e altezza H. Quindi definire sette procedure (con sette condizioni), ciascuna per dividere un problema in due più piccoli e risolverli in modo ricorsivo. È stato dimostrato che qualsiasi problema trattabile soddisfa una delle sette condizioni.
    Rapporto di approssimazione: M (I) <= 2 · OPT (I).

  8. Algoritmo Split-Fit (SF) SF divide gli elementi in due gruppi, L1 con larghezza maggiore di 1/2 e L2 al massimo 1/2. Tutti gli articoli di L1 vengono prima imballati da FFDH. Quindi sono disposti in modo tale che tutti gli elementi con larghezza superiore a 2/3 siano inferiori a quelli con larghezza al massimo 2/3. Questo crea un rettangolo R di spazio con larghezza 1/3. Gli oggetti rimanenti in L2 vengono quindi imballati in R e lo spazio sopra quelli imballati con L1 utilizzando FFDH. I livelli creati in R sono considerati inferiori a quelli creati sopra l'imballaggio di L1.
    Rapporto di approssimazione: SF (I) <= (3/2) · OPT (I) + 2; il limite asintotico di 3/2 è stretto.

  9. L'algoritmo di Sleator L'algoritmo di
    Sleater consiste in quattro passaggi:

    1. Tutti gli articoli di larghezza superiore a 1/2 sono imballati uno sopra l'altro nella parte inferiore della striscia. Supponiamo che h0 sia l'altezza dell'imballaggio risultante Tutti gli imballaggi successivi avverranno al di sopra di h0.

    2. Gli articoli rimanenti sono ordinati per altezza non crescente. Un livello di articoli viene impacchettato (in ordine di altezza non crescente) da sinistra a destra lungo la linea dell'altezza h0.

    3. Una linea verticale viene quindi tracciata nel mezzo per tagliare la striscia in due metà uguali (nota che questa linea può tagliare un oggetto che è imballato parzialmente nella metà destra). Disegna due segmenti di linea orizzontale di una metà, uno sulla metà sinistra (chiamata linea di base sinistra) e uno sulla metà destra (chiamata linea di base destra) il più in basso possibile in modo che le due linee non attraversino alcun elemento.

    4. Scegli la linea di base sinistra o destra di altezza inferiore e impacchetta un livello di oggetti nella metà corrispondente della striscia fino a quando l'elemento successivo non è troppo largo.

    Si forma una nuova linea di base e il passaggio (4) viene ripetuto sulla linea di base inferiore fino a quando tutti gli articoli non vengono imballati.
    Complessità temporale: O (n · log n).
    Il rapporto di approssimazione dell'algoritmo di Sleator è 2.5 che è stretto.


6
Tutto ciò richiede la conoscenza della larghezza dello spazio.
Quantum7,

1
@ Quantum7 forse non è troppo importante poiché OP richiede che i lati siano potenze di due, quindi potremmo semplicemente provare un mucchio di dimensioni con un'area sufficiente.
Ciro Santilli 5 冠状 病 六四 事件 法轮功

19

Dai un'occhiata ai problemi di imballaggio . Penso che il tuo rientri nella "confezione bidone 2D". Dovresti essere in grado di imparare molto dalle soluzioni a questo e altri problemi di imballaggio.

Vedi anche: Imballaggio di dati di immagine rettangolari in una trama quadrata.


Ecco un altro bell'esempio di un algoritmo ottimizzante per l'imballaggio dei rettangoli: codeproject.com/Articles/210979/…
Anderson Green,

Problema menzionato anche in: en.wikipedia.org/wiki/… In particolare, questo limita l'imballaggio del contenitore a un singolo contenitore di dimensioni sconosciute, mi chiedo se sia ancora NP completo.
Ciro Santilli 5 冠状 病 六四 事件 法轮功 il

17

Esiste un'ampia letteratura su questo problema. Una buona euristica avida è quella di posizionare i rettangoli dalla più grande area alla più piccola nella prima posizione disponibile verso il fondo e a sinistra del contenitore. Pensa alla gravità che tira giù tutti gli oggetti nell'angolo in basso a sinistra. Per una descrizione di questo Google "imballaggio in basso a sinistra Chazelle".

Per soluzioni ottimali, le tecniche all'avanguardia possono contenere oltre 20 rettangoli in pochi secondi. Huang ha un algoritmo che separa il problema di trovare il riquadro di delimitazione più piccolo dal problema di decidere se un set di rettangoli può rientrare in un riquadro di delimitazione di dimensioni specifiche. Dai al suo programma una serie di rettangoli e ti dice il più piccolo rettangolo di ingombro necessario per imballarli.

Nel tuo caso, il tuo anello esterno dovrebbe iterare dalla più piccola casella di delimitazione verso l'alto (con la larghezza e l'altezza che aumentano in successione di potenze di due). Per ciascuna di queste caselle di delimitazione, prova per vedere se riesci a trovare un imballaggio per i tuoi rettangoli. Riceverai un sacco di risposte "no", fino alla prima risposta "sì", che sarà garantita per essere la soluzione ottimale.

Per il ciclo interno del tuo algoritmo - quello che risponde "sì" o "no" a un riquadro di delimitazione di dimensioni specifiche, vorrei cercare il riferimento Huang e implementare semplicemente il suo algoritmo. Include molte ottimizzazioni oltre all'algoritmo di base, ma hai davvero solo bisogno di carne e patate di base. Poiché si desidera gestire le rotazioni, in ogni punto del ramo durante la ricerca, provare semplicemente entrambe le rotazioni e il backtrack quando entrambe le rotazioni non risultano in una soluzione.


9

Sono abbastanza sicuro che questo sia un problema NP-difficile , quindi, per una soluzione ottimale, dovresti implementare un algoritmo di backtracking che prova ogni possibile combinazione.

La buona notizia è che a causa della necessità di impacchettare rettangoli 2D in uno spazio 2D limitato, è possibile eliminare molte possibilità all'inizio, quindi potrebbe non essere così male.


3
Probabilmente intendi NP-complete.
Starblue,

7
Bene, se NP è completo, allora è facile da risolvere, basta risolvere un'istanza equivalente del commesso viaggiatore e il gioco è fatto. Ma è banale dimostrare che, come proposto, non lo è, poiché i problemi NP-completi sono problemi di decisione (si ottengono risposte sì / no) e hanno un algoritmo di verifica temporale polinomiale. La domanda "esiste una disposizione di rettangoli a, b, c ... che occupano meno area di 256 * 128 potrebbe essere NP-completa.
Eclipse

2
@Eclipse è corretto. Da jair.org/media/3735/live-3735-6794-jair.pdf "Il problema dell'ottimizzazione è NP-difficile, mentre il problema di decidere se un set di rettangoli può essere impacchettato in un determinato riquadro è NP-completo, tramite una riduzione dall'imballaggio dei rifiuti (Korf, 2003). " Tuttavia, si noti che il PO ha chiesto "un modo abbastanza ottimale", e ci sono soluzioni per questo in P, per definizioni abbastanza ampie di "abbastanza".
Jim Balter,

Sospetto anche la durezza NP, ma abbiamo bisogno di un riferimento / prova.
Ciro Santilli 5 冠状 病 六四 事件 法轮功 il

2
Discussione sacra necro, Batman. Questo è un problema di imballaggio, ed è già stato dimostrato che è NP-difficile nella migliore delle ipotesi: en.wikipedia.org/wiki/Packing_problems
Blindy,

2

Ciò di cui hai bisogno è https://github.com/nothings/stb/blob/master/stb_rect_pack.h

campione:

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 soluzione generale non è banale (la matematica parla del tutto impossibile ****)
Generalmente le persone usano un algoritmo genetico per provare possibili combinazioni, ma puoi fare ragionevolmente bene semplicemente inserendo prima la forma più grande e quindi provando diversi luoghi per il prossimo più grande e così via.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.