Es ist ein Gitter mit einer Größe N x M . Einige Zellen sind Inseln, die mit '0' bezeichnet sind, und die anderen sind Wasser . Auf jeder Wasserzelle befindet sich eine Nummer, die die Kosten einer auf dieser Zelle hergestellten Brücke angibt. Sie müssen die Mindestkosten finden, für die alle Inseln verbunden werden können. Eine Zelle ist mit einer anderen Zelle verbunden, wenn sie eine Kante oder einen Scheitelpunkt teilt.
Mit welchem Algorithmus kann dieses Problem gelöst werden? Was kann als Brute-Force-Ansatz verwendet werden, wenn die Werte von N, M sehr klein sind, beispielsweise NxM <= 100?
Beispiel : In dem gegebenen Bild zeigen grüne Zellen Inseln an, blaue Zellen Wasser und hellblaue Zellen die Zellen, auf denen eine Brücke gebaut werden soll. Für das folgende Bild lautet die Antwort also 17 .
Anfangs dachte ich daran, alle Inseln als Knoten zu markieren und jedes Inselpaar durch eine kürzeste Brücke zu verbinden. Dann könnte das Problem auf Minimum Spanning Tree reduziert werden, aber bei diesem Ansatz habe ich den Fall übersehen, in dem sich Kanten überlappen. In der folgenden Abbildung beträgt der kürzeste Abstand zwischen zwei beliebigen Inseln 7 (gelb markiert). Wenn Sie also Minimum Spanning Trees verwenden, lautet die Antwort 14 , die Antwort sollte jedoch lauten 11 (hellblau markiert) sein.
quelle
Antworten:
Um dieses Problem anzugehen, würde ich ein ganzzahliges Programmierframework verwenden und drei Sätze von Entscheidungsvariablen definieren:
Für die Brückenbaukosten c_ij ist der zu minimierende Zielwert
sum_ij c_ij * x_ij
. Wir müssen dem Modell die folgenden Einschränkungen hinzufügen:y_ijbcn <= x_ij
für jeden Wasserstandort (i, j). Fernery_ijbc1
muss 0 gleich sein, wenn (i, j) nicht an Insel b grenzt. Schließlich kann für n> 1y_ijbcn
nur verwendet werden, wenn in Schritt n-1 ein benachbarter Wasserort verwendet wurde. DefinierenN(i, j)
das Wasser Quadrate zu benachbarten (i, j), dann ist dies äquivalent zuy_ijbcn <= sum_{(l, m) in N(i, j)} y_lmbc(n-1)
.I(c)
die Orte definieren , die an Insel c grenzen, kann dies mit erreicht werdenl_bc <= sum_{(i, j) in I(c), n} y_ijbcn
.sum_{b in S} sum_{c in S'} l_bc >= 1
.Für eine Probleminstanz mit K Inseln, W Wasserquadraten und der angegebenen maximalen Pfadlänge N ist dies ein gemischtes ganzzahliges Programmiermodell mit
O(K^2WN)
Variablen undO(K^2WN + 2^K)
Einschränkungen. Offensichtlich wird dies unlösbar, wenn die Problemgröße groß wird, aber es kann für die Größen, die Sie interessieren, lösbar sein. Um ein Gefühl für die Skalierbarkeit zu bekommen, werde ich es mit dem Pulp-Paket in Python implementieren. Beginnen wir zunächst mit der kleineren 7 x 9-Karte mit 3 Inseln am Ende der Frage:Die Ausführung mit dem Standardlöser aus dem Zellstoffpaket (dem CBC-Löser) dauert 1,4 Sekunden und gibt die richtige Lösung aus:
Betrachten Sie als nächstes das vollständige Problem oben in der Frage, nämlich ein 13 x 14-Raster mit 7 Inseln:
MIP-Löser erhalten oft relativ schnell gute Lösungen und verbringen dann viel Zeit damit, die Optimalität der Lösung zu beweisen. Unter Verwendung des gleichen Solver-Codes wie oben wird das Programm nicht innerhalb von 30 Minuten abgeschlossen. Sie können dem Solver jedoch eine Zeitüberschreitung gewähren, um eine ungefähre Lösung zu erhalten:
Dies ergibt eine Lösung mit dem Zielwert 17:
Um die Qualität der Lösungen zu verbessern, die Sie erhalten, können Sie einen kommerziellen MIP-Solver verwenden (dies ist kostenlos, wenn Sie an einer akademischen Einrichtung sind und ansonsten wahrscheinlich nicht kostenlos). Hier ist zum Beispiel die Leistung von Gurobi 6.0.4, ebenfalls mit einem Zeitlimit von 2 Minuten (obwohl wir aus dem Lösungsprotokoll gelesen haben, dass der Löser innerhalb von 7 Sekunden die derzeit beste Lösung gefunden hat):
Dies findet tatsächlich eine Lösung mit dem objektiven Wert 16, eine, die besser ist, als das OP von Hand finden konnte!
quelle
Ein Brute-Force-Ansatz im Pseudocode:
In C ++ könnte dies wie folgt geschrieben werden
Nach einem ersten Anruf (ich gehe davon aus, dass Sie Ihre 2D-Karten in 1D-Arrays umwandeln, um das Kopieren zu vereinfachen),
bestCost
werden die Kosten für die beste Antwort undbest
das Muster der Brücken, die sie ergeben, enthalten sein. Dies ist jedoch extrem langsam.Optimierungen:
bestCost
, da es keinen Sinn macht, danach weiter zu suchen. Wenn es nicht besser werden kann, graben Sie nicht weiter.Ein allgemeiner Suchalgorithmus wie A * ermöglicht eine viel schnellere Suche, obwohl das Finden besserer Heuristiken keine einfache Aufgabe ist. Für einen problemspezifischeren Ansatz ist die Verwendung vorhandener Ergebnisse für Steiner-Bäume , wie von @Gassa vorgeschlagen, der richtige Weg. Beachten Sie jedoch, dass das Problem beim Bauen von Steiner-Bäumen auf orthogonalen Gittern laut diesem Artikel von Garey und Johnson NP-Complete ist .
Wenn "gut genug" ausreicht, kann ein genetischer Algorithmus wahrscheinlich schnell akzeptable Lösungen finden, solange Sie einige wichtige Heuristiken für die bevorzugte Platzierung der Brücke hinzufügen.
quelle
2^(13*14) ~ 6.1299822e+54
Iterationen. Wenn wir davon ausgehen, dass Sie eine Million Iterationen pro Sekunde ausführen können, würde dies nur ... ~ 19438046000000000000000000000000000000000000 Jahre dauern. Diese Optimierungen sind sehr notwendig.Dieses Problem ist eine Variante des Steiner-Baums, der als knotengewichteter Steiner-Baum bezeichnet wird und auf eine bestimmte Klasse von Graphen spezialisiert ist. Kompakterweise findet der knotengewichtete Steiner-Baum bei einem knotengewichteten ungerichteten Graphen, bei dem einige Knoten Terminals sind, den billigsten Satz von Knoten, einschließlich aller Terminals, die einen verbundenen Untergraphen induzieren. Leider kann ich bei einigen flüchtigen Suchen keine Löser finden.
Um als ganzzahliges Programm zu formulieren, erstellen Sie für jeden nicht-terminalen Knoten eine 0-1-Variable. Für alle Teilmengen von nicht-terminalen Knoten, deren Entfernung aus dem Startdiagramm zwei Terminals trennt, muss die Summe der Variablen in der Teilmenge bei sein mindestens 1. Dies führt zu viel zu vielen Einschränkungen, sodass Sie diese träge durchsetzen müssen, indem Sie einen effizienten Algorithmus für die Knotenkonnektivität (im Grunde maximaler Fluss) verwenden, um eine maximal verletzte Einschränkung zu erkennen. Entschuldigen Sie den Mangel an Details, aber die Implementierung wird schwierig, selbst wenn Sie bereits mit der Ganzzahlprogrammierung vertraut sind.
quelle
Angesichts der Tatsache, dass dieses Problem in einem Raster auftritt und Sie genau definierte Parameter haben, würde ich mich dem Problem mit der systematischen Beseitigung des Problemraums durch Erstellen eines minimalen Spannbaums nähern. Dabei ist es für mich sinnvoll, dieses Problem mit dem Prim-Algorithmus anzugehen.
Leider haben Sie jetzt das Problem, das Raster zu abstrahieren, um eine Reihe von Knoten und Kanten zu erstellen. Das eigentliche Problem dieses Beitrags ist also, wie ich mein nxm-Raster in {V} und {E} konvertiere.
Dieser Umwandlungsprozess ist auf den ersten Blick aufgrund der Vielzahl möglicher Kombinationen wahrscheinlich NP-hart (vorausgesetzt, alle Wasserstraßenkosten sind identisch). Um Fälle zu behandeln, in denen sich Pfade überschneiden, sollten Sie eine virtuelle Insel erstellen.
Wenn dies erledigt ist, führen Sie den Prim-Algorithmus aus und Sie sollten zur optimalen Lösung gelangen.
Ich glaube nicht, dass die dynamische Programmierung hier effektiv ausgeführt werden kann, da es kein beobachtbares Prinzip der Optimalität gibt. Wenn wir die Mindestkosten zwischen zwei Inseln finden, bedeutet dies nicht unbedingt, dass wir die Mindestkosten zwischen diesen beiden und der dritten Insel oder einer anderen Teilmenge von Inseln finden können (nach meiner Definition, um die MST über Prim zu finden). in Verbindung gebracht.
Wenn Sie möchten, dass Code (Pseudo oder auf andere Weise) Ihr Raster in eine Menge von {V} und {E} konvertiert, senden Sie mir bitte eine private Nachricht, und ich werde eine Implementierung zusammenfügen.
quelle