web-dev-qa-db-fra.com

Quel algorithme peut-on utiliser pour tailler des rectangles de différentes tailles dans le plus petit rectangle possible de manière assez optimale?

J'ai un tas d'objets rectangulaires que j'ai besoin d'emballer dans le plus petit espace possible (les dimensions de cet espace doivent être des puissances de deux).

Je connais différents algorithmes d'emballage qui regrouperont les éléments le mieux possible dans un espace donné. Toutefois, dans ce cas, j'ai besoin de cet algorithme pour déterminer la taille de cet espace.

Par exemple, j'ai les rectangles suivants

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

Ils peuvent être emballés dans un espace 128 * 128

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

Cependant, s'il y avait aussi un 160 * 32 et un 64 * 64, il aurait besoin d'un espace de 256 * 128

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

Quels sont les algorithmes capables d’emballer une série de rectangles et de déterminer la taille requise pour le conteneur (d’une puissance de 2 et dans les limites d’une taille maximale donnée pour chaque dimension)?

270
Fire Lancer

La solution de premier passage rapide et sale est toujours une bonne solution pour commencer, à titre de comparaison, sinon rien.

Placement gourmand du grand au petit.

Placez le plus grand rectangle restant dans votre espace compacté. S'il ne peut pas être placé n'importe où, placez-le dans un endroit qui étend la région du sac aussi peu que possible. Répétez l'opération jusqu'à ce que vous ayez fini avec le plus petit rectangle.

Ce n'est pas parfait du tout mais c'est facile et une base de Nice. Cela emballerait toujours parfaitement votre exemple original et vous donnerait une réponse équivalente pour le second également.

65
SPWorley

Voir cette page sur le projet ARC pour un aperçu des solutions, il existe un compromis entre complexité/durée de mise en œuvre et optimalité, mais il existe un large éventail d'algorithmes parmi lesquels choisir.

Voici un extrait des algorithmes:

  1. Algorithme FFDH (First-Fit Decreasing Height)
    FFDH emballe le prochain article R (en hauteur non croissante) au premier niveau où R convient. Si aucun niveau ne peut prendre en charge R, un nouveau niveau est créé.
    Complexité temporelle de FFDH: O (n · log n).
    Ratio approximatif: FFDH (I) <= (17/10) · OPT (I) +1; la limite asymptotique de 17/10 est serrée.

  2. Algorithme NFDH (Next-Fit Decreasing Height)
    NFDH emballe l’article suivant R (en hauteur non croissante) au niveau actuel si R s’ajuste. Sinon, le niveau actuel est "fermé" et un nouveau niveau est créé.
    Complexité temporelle: O (n · log n).
    Ratio approximatif: NFDH (I) <= 2 · OPT (I) +1; la borne asymptotique de 2 est serrée.

  3. Algorithme BFDH (Best-Fit Decreasing Height)
    BFDH regroupe l’article suivant R (en hauteur non croissante) au niveau, parmi ceux pouvant accueillir R, pour lequel l’espace horizontal résiduel est le minimum. Si aucun niveau ne peut prendre en charge R, un nouveau niveau est créé.

  4. Algorithme Bas-Gauche (BL)
    BL articles de premier ordre par largeur non croissante. BL emballe le prochain article aussi près du bas que possible, puis aussi près de la gauche que possible, sans chevauchement avec aucun article emballé. Notez que BL n'est pas un algorithme de compression orienté niveau.
    Complexité temporelle: O (n ^ 2).
    Ratio approximatif: BL (I) <= 3 · OPT (I).

  5. Algorithme Up-Down (UD) de Baker
    UD utilise une combinaison de BL et une généralisation de NFDH. La largeur de la bande et les articles sont normalisés afin que la bande ait une largeur unitaire. UD ordonne les articles en largeur non croissante, puis les divise en cinq groupes, chacun d’une largeur comprise entre 1/2 (1), (1/3,1/2) et (1/4,1/3). ], (1/5,1/4], (0,1/5]. La bande est également divisée en cinq régions R1, ···, R5. En gros, certains éléments de largeur comprise dans la plage (1/i + 1, 1/i], pour 1 <= i <= 4, sont compressés dans la région Ri par BL, BL laissant un espace de plus en plus large du côté droit de la bande, UD profite de cet avantage en premier emballage de l'article dans Rj pour j = 1, ···, 4 (dans l'ordre) de haut en bas. S'il n'y a pas d'espace disponible, l'article est emballé dans Ri par BL. Enfin, les articles de taille maximale 1/5 sont compressés dans les espaces de R1, ···, R4 par l'algorithme (généralisé) NFDH. Là encore, s'il n'y a pas d'espace dans ces régions, l'élément est compressé dans R5 à l'aide de NFDH.
    Ratio approximatif: UD (I) <= (5/4) · OPT (I) + (53/8) H, où H est la hauteur maximale des éléments; la limite asymptotique de 5/4 est serrée.

  6. Algorithme d'ajustement inverse (RF)
    RF normalise également la largeur de la bande et des éléments de sorte que la bande ait une largeur unitaire. RF commence par empiler tous les éléments de largeur supérieure à 1/2. Les éléments restants sont triés selon une hauteur non croissante et seront emballés au-dessus de la hauteur H0 atteinte par ceux supérieurs à 1/2. Then = RF répète le processus suivant. En gros, RF emballe les éléments de gauche à droite avec leur bas le long de la ligne de hauteur H0 jusqu'à ce qu'il n'y ait plus de place. Ensuite, emballe les éléments de droite à gauche et de haut en bas (niveau inversé) jusqu’à ce que la largeur totale soit au moins égale à 1/2, puis est abaissée jusqu’à ce que (au moins) l’un d’entre eux touche un élément inférieur. descendre est en quelque sorte répété.
    Ratio approximatif: RF (I) <= 2 · OPT (I).

  7. Algorithme de Steinberg
    L'algorithme de Steinberg, noté M dans le document, estime une limite supérieure de la hauteur H requise pour emballer tous les éléments de sorte qu'il est prouvé que les éléments d'entrée peuvent être emballés dans un rectangle de largeur W et de hauteur H Ils définissent ensuite sept procédures (avec sept conditions), chacune permettant de diviser un problème en deux plus petites et de les résoudre récursivement. Il a été démontré que tout problème traitable répond à l’une des sept conditions.
    Ratio approximatif: M(I) <= 2 · OPT (I).

  8. Algorithme Split-Fit (SF) SF divise les éléments en deux groupes, L1 avec une largeur supérieure à 1/2 et L2 au maximum 1/2. Tous les articles de L1 sont d'abord emballés par FFDH. Ensuite, ils sont disposés de manière à ce que tous les articles de largeur supérieure à 2/3 soient inférieurs à ceux de largeur supérieure à 2/3. Cela crée un rectangle R d’espace de largeur 1/3. Les articles restants dans L2 sont ensuite emballés dans R et l'espace au-dessus de ceux contenant L1 à l'aide de FFDH. Les niveaux créés dans R sont considérés comme inférieurs à ceux créés au-dessus de l'emballage de L1.
    Ratio approximatif: SF (I) <= (3/2) · OPT (I) + 2; la borne asymptotique de 3/2 est serrée.

  9. Algorithme de Sleator
    L'algorithme de Sleater comprend quatre étapes:

    1. Tous les articles de largeur supérieure à 1/2 sont emballés les uns sur les autres au bas de la bande. Supposons que h0 soit la hauteur de l'emballage résultant. Tous les emballages ultérieurs auront lieu au-dessus de h0.

    2. Les articles restants sont classés par hauteur non croissante. Un niveau d'articles est emballé (dans l'ordre croissant des hauteurs) de gauche à droite le long de la ligne de hauteur h0.

    3. Une ligne verticale est ensuite tracée au milieu pour couper la bande en deux moitiés égales (notez que cette ligne peut couper un article emballé partiellement dans la moitié droite). Tracez deux segments de ligne horizontale de longueur, une moitié, un dans la moitié gauche (appelée ligne de base gauche) et un autre dans la moitié droite (appelée ligne de base droite) aussi bas que possible de sorte que les deux lignes ne croisent aucun élément.

    4. Choisissez la ligne de base gauche ou droite qui est d'une hauteur inférieure et insérez un niveau d'éléments dans la moitié correspondante de la bande jusqu'à ce que l'élément suivant soit trop large.

    Une nouvelle ligne de base est formée et l'étape (4) est répétée sur la ligne de base inférieure jusqu'à ce que tous les articles soient emballés.
    Complexité temporelle: O (n · log n).
    Le rapport d'approximation de l'algorithme de Sleator est 2,5, ce qui est serré.

84
Undefined Behavior

Jetez un oeil à problèmes d'emballage . Je pense que le vôtre est classé dans la catégorie "bin bin 2D". Vous devriez être en mesure d'apprendre beaucoup de solutions et d'autres problèmes d'emballage.

Voir aussi: Emballage de données d'image rectangulaires dans une texture carrée.

19
aib

Il existe une littérature abondante sur ce problème. Une bonne heuristique gourmande consiste à placer des rectangles du plus grand au plus petit dans la première position disponible vers le bas et la gauche du conteneur. Pensez à la gravité en tirant tous les objets vers le coin inférieur gauche. Pour une description de ce google "Chazelle en bas à gauche".

Pour des solutions optimales, les techniques de pointe permettent d’empaqueter 20 rectangles en quelques secondes. Huang a un algorithme qui sépare le problème de la recherche de la plus petite boîte englobante du problème consistant à décider si un ensemble de rectangle peut s’intégrer ou non dans une boîte englobante d’une taille spécifique. Vous donnez à son programme un ensemble de rectangles, et il vous indique la plus petite boîte englobante requise pour les emballer.

Dans votre cas, votre boucle externe doit parcourir la plus petite boîte englobante possible (la largeur et la hauteur augmentant progressivement par deux). Pour chacune de ces boîtes, essayez de voir si vous pouvez trouver un emballage pour vos rectangles. Vous obtiendrez un tas de réponses "non", jusqu'à la première réponse "oui", qui sera garantie comme étant la solution optimale.

Pour la boucle interne de votre algorithme - celle qui répond par "oui" ou par "non" à un cadre de sélection de taille spécifique, je voudrais rechercher la référence Huang et mettre en œuvre son algorithme. Il inclut de nombreuses optimisations en plus de l'algorithme de base, mais vous n'avez besoin que de la viande et des pommes de terre de base. Étant donné que vous souhaitez gérer les rotations, essayez à la fois les rotations et les retours en arrière à chaque point de la branche au cours de votre recherche, lorsque les deux rotations ne donnent pas de solution.

17
Eric

Je suis assez certain que c'est n problème NP-difficile , donc, pour une solution optimale, vous devez implémenter un algorithme de retour en arrière qui tente toutes les combinaisons possibles.

La bonne nouvelle est qu’étant donné qu’il est nécessaire d’emballer des rectangles 2D dans un espace 2D limité, vous pouvez tailler beaucoup de possibilités très tôt, pour que ce ne soit peut-être pas si mal que ça.

9
Blindy

Ce dont vous avez besoin est à https://github.com/nothings/stb/blob/master/stb_rect_pack.h

échantillon:

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

Une solution générale n’est pas triviale (les maths parlent de tout à fait impossible)
Généralement, les gens utilisent un algorithme génétique pour essayer les combinaisons possibles, mais vous pouvez raisonnablement vous en tirer en commençant par placer la forme la plus grande en premier, puis en essayant différents endroits pour la suivante, etc.

1
Martin Beckett

J'utilise celui de:

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

Il implémente l'algorithme guillotine et requiert comme entrée une dimension et tente d'optimiser l'autre (vous pouvez également définir un maximum avec une petite modification du code). Peut-être que si vous essayez différents pouvoirs de deux valeurs, cela fonctionnera pour vous.

Ce n'est pas optimal du tout, mais c'est petit, portable (.h seulement), et n'a pas d'autre dépendance que C++ et STL.

0
Adriel Jr