Algorithmus zum "Heilen" mehrerer Rechtecke in eine kleinere Anzahl von Rechtecken?

13

Geben Sie hier die Bildbeschreibung ein

Angenommen, ich habe ein Raster aus Rechtecken mit verschiedenen Formen und Farben und möchte die Anzahl der Rechtecke reduzieren (einigermaßen nahe am Optimum ist in Ordnung, Optimal ist nicht erforderlich), um das gleiche Farblayout darzustellen.

Das obige Bild ist ein sehr vereinfachter Fall, und das Leerzeichen zwischen den Rechtecken dient nur zur Visualisierung - sie wären tatsächlich dicht gepackt.

Was ist ein Ansatz- oder Algorithmusname (gerne googeln), der mir dabei helfen kann?

xaxxon
quelle
3
Können Sie uns etwas darüber erzählen, woher diese Rechtecke kommen? Neigen sie dazu, sich (grob) an einem zugrunde liegenden Gitter auszurichten oder einen gemeinsamen Baustein oder ein kleinstes "Atom" -Rechteck zu teilen? Können sie gedreht werden? Dies scheint ein Problem zu sein, das im allgemeinsten Fall sehr heikel sein kann, aber möglicherweise viel einfacher wird, wenn wir einige Einschränkungen oder Gemeinsamkeiten in Ihrem speziellen Szenario ausnutzen können.
DMGregory
Es gibt ein darunterliegendes Gitter aus Quadraten (wie ein Schachbrett) und jedes Rechteck teilt Grenzen mit diesen darunter liegenden Quadraten. Das heißt, Sie können eine Ganzzahl verwenden, um oben / unten / links / rechts von jedem Rechteck zu beschreiben. Daher können sie nicht in Winkeln gedreht werden, die nicht um 90 Grad teilbar sind. Auch das NxM-Gitter ist vollständig mit Rechtecken gefüllt - es gibt keine unbedeckten Gitterpositionen.
Xaxxon
Ich versuche nur, den Fall zu vermeiden, der wie im obigen Beispiel aussieht (aus Sicht der Färbung), aber er besteht aus einer Tonne 1x1-Rechtecken und ich verarbeite jedes einzelne davon, wenn ich den Raum in vielen bearbeiten kann weniger Anrufe.
Xaxxon
Ich vermute eine Art von "Fangen Sie einfach irgendwo an und versuchen Sie immer größere Rechtecke in einer Dimension (z. B. vertikal), bis Sie einen Farbrand erreichen, und vergrößern Sie dann die andere Dimension (horizontal), bis Sie einen Rand erreichen. Versuchen Sie es dann zuerst horizontal Dann versuchen Sie vielleicht nur Quadrate (diagonal wachsend). Aber nicht sicher, ob es der richtige Ansatz ist, einfach die größte der oben genannten 3 Möglichkeiten auszuwählen.
xaxxon
Ist es akzeptabel, ein vorhandenes Rechteck zu teilen, wenn es am Ende zu weniger Rechtecken führt? Oder sollte der Algorithmus immer nur zusammengeführt werden? Ist die Gesamtzahl auch das einzige Kriterium, oder bevorzugen Sie quadratischere Formen gegenüber langen, dünnen Splittern / größeren Rechtecken gegenüber kleineren?
DMGregory

Antworten:

15

Erstens können wir Ihre Quellrechtecke in Zellen in Ihrem zugrunde liegenden Raster konvertieren, um die Eingabe einheitlicher zu gestalten. (Effektives Rasterisieren des Problems)

Auf diese Weise können wir Optimierungen finden, die bei der direkten Arbeit mit den Quellrechtecken möglicherweise nicht offensichtlich sind - insbesondere, wenn mehrere Quellrechtecke geteilt werden müssen, um sie unterschiedlich zu kombinieren.

Beispiel zum Konvertieren von Rechtecken in Gitterzellen und zurück

Als nächstes können wir verbundene Bereiche derselben Farbe mithilfe von Tiefen-First-Search- oder Flood-Filling-Algorithmen finden. Wir können jede verbundene Region (ein Polyomino ) isoliert betrachten - nichts, was wir einer anderen Region antun, muss diese beeinflussen.

Eigentlich wollen wir einen Weg finden, dieses Polyomino in Rechtecke zu zerlegen (leider geht es in der meisten Literatur, die ich finden kann, um das gegenteilige Problem: das Zerlegen von Rechtecken in Polyominos! Dies macht es schwierig, nach Leads zu suchen ...)

Eine einfache Methode besteht darin, horizontale Läufe benachbarter Quadrate zu langen, dünnen Rechtecken zu kombinieren. Dann können wir mit der obigen Zeile vergleichen und kombinieren, ob Start und Ende unseres Laufs übereinstimmen - entweder wenn wir jeden Lauf / jede Zeile beenden oder wenn wir jede Zelle als zum aktuellen Lauf hinzufügend betrachten.

Zerlegen eines Polyominos in horizontale Läufe und anschließendes vertikales Zusammenführen

Ich weiß noch nicht, wie nahe diese Methode dem Optimum kommt. Es scheint, dass es zu Problemen kommen kann, wenn eine Zeile, die noch nicht berücksichtigt wurde, eine andere Aufteilung vorschlägt als die Zeilen, die bisher gesehen wurden:

Beispiel eines Falls mit einer 3-Rechteck-Lösung, bei dem die obige Methode 4 ergibt

Das Erkennen, wann ein Lauf / Rechteck genau von Läufen oben und unten abgedeckt wird, das Aufteilen und Zusammenführen dieser Läufe löst diesen speziellen Fall, aber ich habe nicht untersucht, wie allgemein das Problem ist.

Ich habe mir auch Methoden angesehen, bei denen wir den Umfang des Polyominos durchlaufen und jedes Mal, wenn wir auf eine konkave Ecke stoßen, überqueren, aber dieser Ansatz erscheint mir fehleranfälliger. Um optimale Ergebnisse zu erzielen, müssen anscheinend Schnitte priorisiert werden, die zwei konkave Ecken verbinden, und Formen, die Vertiefungen enthalten, müssen speziell behandelt werden, sodass die Zeilenscanmethode den Vorteil der Einfachheit zu haben scheint.

Eine weitere Methode, die ich mir anschaue, besteht darin, den ersten Lauf in der oberen Reihe zu nehmen und ihn so weit wie möglich nach unten zu verlängern. Nehmen Sie dann den ersten Lauf in der obersten Reihe der verbleibenden Teile. Dies wird jedoch bei umgekehrten T-Formen ausgelöst, sodass es auch nicht optimal ist.

Ich habe das Gefühl, dass es wahrscheinlich eine Möglichkeit gibt, mithilfe der dynamischen Programmierung die optimale Aufteilung zu finden, aber ich habe sie noch nicht gefunden.

DMGregory
quelle
Danke für die tolle Antwort! Diese Lösung sieht schnell genug aus, dass ich sie in verschiedene Richtungen ausführen und auswählen kann, welche am besten erscheint - horizontal links-> rechts, horizontal rechts-> links und dann auch vertikal in jede Richtung.
Xaxxon
2
Das Problem ist, dass wir Formen konstruieren können, die den Algorithmus aus jeder Sweep-Richtung irreführen. Diese werden wahrscheinlich nicht im wirklichen Gebrauch auftauchen, aber es nervt mich trotzdem. Ich denke, es gibt noch eine einfache Lösung ... So etwas wie bei jedem Lauf zu notieren, ob es während des Laufs konkave Ecken darüber gibt. Wenn dann ein nachfolgender Lauf genau an einem solchen Punkt endet, kehren wir durch die Läufe zurück und teilen sie vertikal auf. Ich habe jedoch nicht alle Details geklärt.
DMGregory
1
Ich bin mir auch nicht sicher, warum der Flutfüllschritt notwendig ist. Wenn Sie von einer Rasterposition zu einem langen, dünnen Rechteck wechseln, können Sie einfach die gesamte Zeile oder Spalte des Rasters (je nachdem, in welche Richtung Sie gehen) durchlaufen, um diese 1xN-Rechtecke zu erstellen. Sie müssen den Polyomino nie kennen, oder?
xaxxon
Sie haben Recht, die Flutfüllung ist kein notwendiger Schritt. Ich habe es eingefügt, um zu rechtfertigen, dass in den folgenden Schritten jeweils nur ein farbiger Bereich fokussiert wird. Sie können die Zeilenscan-Methode jedoch problemlos auf mehrere verschachtelte farbige Bereiche anwenden. Die perimeterbasierte Methode muss jedoch jeweils am Umfang einer Form arbeiten.
DMGregory