9
votes

Algorithme pour maximiser la couverture de la zone rectangulaire avec des carreaux de mise à l'échelle

J'ai n tuiles carrées évolutives (boutons) qui doivent être placées à l'intérieur de la surface rectangulaire de taille fixe (boîte à outils). Je voudrais présenter les boutons tous à la même taille.

Comment puis-je résoudre la taille optimale des carreaux qui fourniraient la plus grande surface de la surface rectangulaire recouverte de tuiles.


5 commentaires

Avez-vous une taille fixe pour la boîte à outils dans les directions horizontales et verticales? Avez-vous une taille minimale et / ou maximale pour les boutons?


@ebbynum Oui, X et Y sont prédéfinis. Et oui, il y a une taille maximale pour les boutons, mais si la taille optimale se trouve être supérieure au maximum, je pouvais simplement utiliser le max à la place (de sorte que cela ne change pas vraiment le problème IMO).


J'aimerais simplement souligner que cela a une application commerciale énorme si elle est appliquée à des formes polygonales non rectangulaires. Les produits «nid» des formes pour une densité de produit maximale sont toujours en demande pour l'industrie manufacturière.


@enens vous avez dû dire "l'industrie de la fabrication carrée". C'est comme dire que la résolution de 2SAT a une application énorme à SAT. Non, le général s'applique au spécifique, pas l'inverse.


@PicColbo Je pensais plus sur la coupe / des découpes en acier en acier. Le moins squelette vous laissez mieux votre marge bénéficiaire. Des produits tels que SigManest qui tentent d'optimiser le motif coupé augmentent considérablement le retour sur investissement de la plupart des opérations de coupe en acier. Mais le principe s'applique à toutes les pièces de coupe d'entreprise en matière de matériel de feuille.


7 Réponses :


0
votes

La première heuristique très rugueuse est de prendre xxx

s est la longueur de bouton-pression, x et < Code> y est la largeur et la hauteur de la boîte à outils et n est le nombre de boutons.

Dans ce cas, s sera la longueur maximale possible. Il n'est pas nécessairement possible de cartographier cet ensemble de boutons sur la barre d'outils, cependant.

Imaginez une barre d'outils de 20 unités par 1 unité avec 5 boutons. L'heuristique vous donnera une longueur de côté de 2 (surface de 4), avec une surface couverte totale de 20. Cependant, la moitié de chaque bouton sera en dehors de la barre d'outils.


0 commentaires

0
votes

Je prendrais une approche itérative ici. Je vérifierais s'il est possible d'adapter tout bouton en une seule ligne. Sinon, vérifiez s'il est possible d'installer deux rangées, etc.

dire w est le plus petit côté de la boîte à outils. H est l'autre côté.

Pour chaque itération, je vérifierais les meilleurs cas et les plus pires possibles, dans cet ordre. Meilleur affaire signifie que c'est la nième itération, essayerait une taille de boutons de tailles W / N x W / N. Si la valeur H est suffisante, nous sommes terminés. Sinon, le pire des cas est d'essayer (w / (n + 1)) + 1 x (w / (n + 1)) + 1 boutons de taille. S'il est possible d'adapter tous les boutons, j'essaierais un procédé de bisection entre W / N et (W / (N + 1)) + 1. Si non, l'itération continue à N + 1.


0 commentaires

14
votes

let w et h être la largeur et la hauteur du rectangle.

let s soit la longueur du côté de carré.

puis le nombre de carrés n (s) que vous pouvez installer dans le rectangle est plancher (w / s) * plancher (H / s) < / code>. Vous souhaitez trouver la valeur maximale de S pour laquelle n (s)> = n

Si vous tracez le nombre de carrés contre s Vous obtiendrez une fonction constante par morceaux. Les discontinuités sont aux valeurs w / i et h / j , où i et j traversent le positif nombres entiers.

Vous voulez trouver le plus petit i pour lequel n (w / i)> = n , et similaire le plus petit j pour lequel n (h / j)> = n . Appelez ces plus petites valeurs i_min et j_min . Ensuite, le plus grand des w / i_min et h / j_min est le s que vous voulez.

I.e. s_max = max (w / i_min, h / j_min)

pour trouver i_min et j_min , juste faire une brute Recherche de force: Pour chacun, commencez par 1, test et incrément.

dans l'événement que n est très grand, il peut être désagréable de rechercher le i s et j à partir de 1 (bien qu'il soit est difficile à imaginer qu'il y aura une différence notable de la performance). Dans ce cas, nous pouvons estimer les valeurs de départ comme suit. Premièrement, une estimation de la zone de la zone d'une tuile est w * h / n , correspondant à un côté de sqrt (w * h / n) . Si w / i <= sqrt (w * h / n) , alors i> = CEIL (w * sqrt (n / (w * h))) , de la même manière j> = ceil (h * sqrt (n / (w * h)))

donc, plutôt que de démarrer les boucles au i = 1 et j = 1 , nous pouvons les démarrer à i = ceil (sqrt (n * w / h)) et j = ceil (sqrt (n * h / W)))) . Et OP suggère que rond fonctionne mieux que ceil - au pire une itération supplémentaire.

Voici l'algorithme orthographié en C ++: xxx

Ce qui précède est écrit pour plus de clarté. Le code peut être considérablement serré comme suit: xxx


9 commentaires

@Kendall, y a-t-il une raison pour laquelle cette solution ne fonctionne pas pour vous?


@Brainjamin, pouvez-vous l'exécuter sur un exemple pour nous? Si l'optimal I est différent de l'optimal J, que S est différent de l'art. Mais si W >> H c'est évidemment le cas. Je pense donc que votre méthode est contradictoire.


@PicColbo, j'ai édité la dernière partie de la réponse pour essayer de clarifier l'algorithme .. J'ai introduit i_min, j_min, s_max pour distinguer l'extrema réelle.


@Brainjam J'espérais une solution de force non brute (dans certains cas, le N peut être très grand). Je pense que j'ai mangé une façon de deviner où est le point optimal. De plus, votre solution est assez naïve. Cela ne fait rien de plus que d'essayer toutes les possibilités, puis de sélectionner le meilleur.


@Kendall, assez juste. J'ai ajouté une estimation pour le début i et j. Dans mes expériences limitées, j'ai constaté que les estimations sont précises ou éteintes par une.


Ce problème a soit une solution fermée ou non. Si c'est une solution fermée, nous obtenons une formule et c'est la fin. S'il n'est pas fermé, nous effectuons une recherche comme dans chaque problème d'optimisation non fermé. Je pense que cela étant un problème entier n'aura pas une solution de forme fermée et sera en fait dure NP-Hard, une recherche est donc en ordre. @Brainjam a proposé une bonne recherche qui obtiendrait la bonne réponse. Nous pouvons soutenir que ce soit la meilleure recherche possible. Peut-être que vous pouvez commencer l'itération de I et J à des endroits plus optimaux que 1. Peu importe ce qu'il y aura une recherche impliquée.


@Brainjam c'est la même formule que j'ai proposée dans mes tests (j'ai trouvé que rond a été légèrement meilleur que ceil ). Pourriez-vous ajuster votre code pour refléter ces changements?


@Brainjam Pourriez-vous regarder le code dans votre dernier édition. Il a quelques petites numéros de syntaxe et de formatage. J'accepterai votre réponse quand c'est fait. Merci pour tout votre travail :)


@Kendall, j'ai réparé les fautes de frappe et je me suis assuré que le code construit et fonctionne.



0
votes

Soit N (s) être le nombre de carrés pouvant correspondre à leur côté. Soit w, h être les côtés du rectangle à remplir. Puis n (s) = plancher (w / s) * plancher (H / s). Il s'agit d'une fonction décroissante monotone à S et de constante par morceaux, de sorte que vous pouvez effectuer une légère modification de la recherche binaire pour trouver les plus petits s tels que n (s) n (s)> = n mais n (s + eps) = n Puis l = min (w / plancher (w / t), H / plancher (H / t)) sinon u = max (w / plancher (w / t), H / plancher (H / t)). Arrêtez-vous quand U et L restez le même dans des itérations consécutives. C'est donc comme une recherche binaire, mais vous exploitez le fait que la fonction est constante par morceaux et que les points de changement sont lorsque W ou H sont un multiple exact de s. Nice petit problème, merci de la proposer.


0 commentaires

0
votes

Nous savons que toute solution optimale (il peut y avoir deux) remplira le rectangle horizontalement ou verticalement. Si vous avez trouvé une solution optimale qui n'a pas rempli le rectangle dans une dimension, vous pouvez toujours augmenter l'échelle des tuiles pour remplir une dimension.

Maintenant, toute solution qui optimise la surface couverte aura un rapport de format proche du rapport de format du rectangle. Le rapport de format de la solution est Compte de tuile vertical / Nombre de carreaux horizontaux (et le rapport de format du rectangle est Y / x ).

Vous pouvez simplifier le problème en forçant y> = x ; En d'autres termes, si x> y , transpose le rectangle. Cela vous permet de penser que des rapports d'aspect> = 1, tant que vous vous souvenez de transposer la solution.

Une fois que vous avez calculé le format de format, vous souhaitez trouver des solutions au problème de v / h ~ = y / x , où v est la tuile verticale Compte et H est le nombre horizontal des tuiles. Vous trouverez jusqu'à trois solutions: le plus proche v / h à y / x et v + 1 , v-1 < / code>. À ce stade, calculez simplement la couverture basée sur la balance à l'aide de V et h et prenez le maximum (il pourrait y en avoir plus d'un).


3 commentaires

Votre déclaration initiale n'est pas correcte. Si j'ai 4 carreaux et une surface 5x5, ma taille carrée optimale sera 2x2, couvrant un total de 4x4 de la 5x5, non? 3x3 carrés (9 par) couvrira 36 unités totales et la surface n'est que de 25 unités totales.


@ebynum, j'allais sous l'hypothèse que les échelles n'étaient pas des entiers. S'ils sont, alors je ne sais pas si ma solution est optimale. S'ils ne sont pas, alors le mien est.


Si je comprends le problème, les tuiles ne sont pas des entiers, car elles sont des boutons sur une boîte à outils. OK, peut-être qu'ils sont des entiers après tout (pixels), mais comme la taille de pixels peut être considérée comme beaucoup plus petite que la taille de la boîte à outils, le problème est continu (c'est-à-dire dans R)



9
votes

Je pense que cela peut être résolu comme un problème de minimisation contraint, ce qui nécessite un calcul de base. .

Définitions: p> xxx pré>

Vous devez minimiser la fonction: p> xxx pré>

Sous réserve des contraintes: P >

  f[a_, l_, k_] :=  NMinimize[{a l/s^2 - k , 
                               IntegerPart[a/s] IntegerPart[l/s] - k >= 0, 
                               s > 0}, 
                               {s}]     


3 commentaires

La fonction objectif ne devrait-il pas être a * l - k * s ^ 2 ? Pour représenter la contrainte d'origine que la plus grande zone est recouverte de tuiles? Votre fonction objectif est en termes de nombre de carrés qui pourraient devenir funky lorsque le nombre de carrés doit être un nombre entier.


@marimon, vous pouvez penser que les deux manières ... Les résultats sont les mêmes (déjà couru le PROG)


Je pense juste que les conditions de Kuhn Tucker me font mal à la tête!



4
votes

Je réalise que c'est un vieux fil, mais j'ai récemment résolu ce problème de manière à ce que je pense soit efficace et donne toujours la bonne réponse. Il est conçu pour maintenir un rapport d'aspect donné. Si vous souhaitez que les enfants (boutons dans ce cas) soient carrés, utilisez simplement un rapport de format de 1. Je suis actuellement en train d'utiliser cet algorithme dans quelques endroits et cela fonctionne bien.

        double VerticalScale; // for the vertical scalar: uses the lowbound number of columns
        double HorizontalScale;// horizontal scalar: uses the highbound number of columns
        double numColumns; // the exact number of columns that would maximize area
        double highNumRows; // number of rows calculated using the upper bound columns
        double lowNumRows; // number of rows calculated using the lower bound columns
        double lowBoundColumns; // floor value of the estimated number of columns found
        double highBoundColumns; // ceiling value of the the estimated number of columns found


        Size rectangleSize = new Size(); // rectangle size will be used as a default value that is the exact aspect ratio desired.
        //
        // Aspect Ratio = h / w
        // where h is the height of the child and w is the width
        //
        // the numerator will be the aspect ratio and the denominator will always be one
        // if you want it to be square just use an aspect ratio of 1
        rectangleSize.Width = desiredAspectRatio;
        rectangleSize.Height = 1;

        // estimate of the number of columns useing the formula:
        //                          n * W * h       
        //  columns = SquareRoot(  -------------  )
        //                            H * w          
        //
        // Where n is the number of items, W is the width of the parent, H is the height of the parent,
        // h is the height of the child, and w is the width of the child
        numColumns = Math.Sqrt( (numRectangles * rectangleSize.Height * parentSize.Width) / (parentSize.Height * rectangleSize.Width) );

        lowBoundColumns = Math.Floor(numColumns);
        highBoundColumns = Math.Ceiling(numColumns);

        // The number of rows is determined by finding the floor of the number of children divided by the columns
        lowNumRows = Math.Ceiling(numRectangles / lowBoundColumns);
        highNumRows = Math.Ceiling(numRectangles / highBoundColumns);

        // Vertical Scale is what you multiply the vertical size of the child to find the expected area if you were to find
        // the size of the rectangle by maximizing by rows
        //
        //                      H
        // Vertical Scale = ----------
        //                    R * h
        //
        // Where H is the height of the parent, R is the number of rows, and h is the height of the child
        //
        VerticalScale = parentSize.Height / lowNumRows * rectangleSize.Height;

        //Horizontal Scale is what you multiply the horizintale size of the child to find the expected area if you were to find
        // the size of the rectangle by maximizing by columns
        //
        //                      W
        // Vertical Scale = ----------
        //                    c * w
        //
        //Where W is the width of the parent, c is the number of columns, and w is the width of the child
        HorizontalScale = parentSize.Width / (highBoundColumns * rectangleSize.Width);

        // The Max areas are what is used to determine if we should maximize over rows or columns
        //  The areas are found by multiplying the scale by the appropriate height or width and finding the area after the scale
        //                      
        // Horizontal Area =  Sh * w * ( (Sh * w) / A )
        //                     
        // where Sh is the horizontal scale, w is the width of the child, and A is the aspect ratio of the child
        //
        double MaxHorizontalArea = (HorizontalScale * rectangleSize.Width) * ((HorizontalScale * rectangleSize.Width) / desiredAspectRatio);
        //
        //                       
        // Vertical Area =   Sv * h * (Sv * h) * A
        // Where Sv isthe vertical scale, h is the height of the child, and A is the aspect ratio of the child
        //
        double MaxVerticalArea = (VerticalScale * rectangleSize.Height) * ((VerticalScale * rectangleSize.Height) * desiredAspectRatio);


        if (MaxHorizontalArea >= MaxVerticalArea ) // the horizontal are is greater than the max area then we maximize by columns
        {
            // the width is determined by dividing the parent's width by the estimated number of columns
            // this calculation will work for NEARLY all of the horizontal cases with only a few exceptions
            newSize.Width = parentSize.Width / highBoundColumns; // we use highBoundColumns because that's what is used for the Horizontal
            newSize.Height = newSize.Width / desiredAspectRatio; // A = w/h or h= w/A

            // In the cases that is doesnt work it is because the height of the new items is greater than the 
            // height of the parents. this only happens when transitioning to putting all the objects into
            // only one row
            if (newSize.Height * Math.Ceiling(numRectangles / highBoundColumns) > parentSize.Height)
            {
                //in this case the best solution is usually to maximize by rows instead
                double newHeight = parentSize.Height / highNumRows;
                double newWidth = newHeight * desiredAspectRatio;

                // However this doesn't always work because in one specific case the number of rows is more than actually needed
                // and the width of the objects end up being smaller than the size of the parent because we don't have enough
                // columns
                if (newWidth * numRectangles < parentSize.Width)
                {
                    //When this is the case the best idea is to maximize over columns again but increment the columns by one
                    //This takes care of it for most cases for when this happens.
                    newWidth = parentSize.Width / Math.Ceiling(numColumns++);
                    newHeight = newWidth / desiredAspectRatio;

                    // in order to make sure the rectangles don't go over bounds we
                    // increment the number of columns until it is under bounds again.
                    while (newWidth * numRectangles > parentSize.Width)
                    {
                        newWidth = parentSize.Width / Math.Ceiling(numColumns++);
                        newHeight = newWidth / desiredAspectRatio;
                    }

                    // however after doing this it is possible to have the height too small.
                    // this will only happen if there is one row of objects. so the solution is to make the objects'
                    // height equal to the height of their parent
                    if (newHeight > parentSize.Height)
                    {
                        newHeight = parentSize.Height;
                        newWidth = newHeight * desiredAspectRatio;
                    }
                }

                // if we have a lot of added items occaisionally the previous checks will come very close to maximizing both columns and rows
                // what happens in this case is that neither end up maximized

                // because we don't know what set of rows and columns were used to get us to where we are
                // we must recalculate them with the current measurements
                double currentCols = Math.Floor(parentSize.Width / newWidth); 
                double currentRows = Math.Ceiling(numRectangles/currentCols);
                // now we check and see if neither the rows or columns are maximized
                if ( (newWidth * currentCols ) < parentSize.Width && ( newHeight * Math.Ceiling(numRectangles/currentCols) ) < parentSize.Height)
                {
                    // maximize by columns first
                    newWidth = parentSize.Width / currentCols;
                    newHeight = newSize.Width / desiredAspectRatio;

                    // if the columns are over their bounds, then maximized by the columns instead
                    if (newHeight * Math.Ceiling(numRectangles / currentCols) > parentSize.Height)
                    {
                        newHeight = parentSize.Height / currentRows;
                        newWidth = newHeight * desiredAspectRatio;
                    }
                }

                // finally we have the height of the objects as maximized using columns
                newSize.Height = newHeight;
                newSize.Width = newWidth;

            }

        }
        else
        {
            //Here we use the vertical scale. We determine the height of the objects based upong
            // the estimated number of rows.
            // This work for all known cases
            newSize.Height = parentSize.Height / lowNumRows; 
            newSize.Width = newSize.Height * desiredAspectRatio;
        }


2 commentaires

Vous pouvez voir l'explication complète de mon code ici: codeProject.com/kb/recipes/tilcalingcode .aspx


Merci! S'il vous plaît, comment allez-vous ajouter un rembourrage / marge / écart entre les enfants rectangulaires?