Ich hatte einige Probleme damit, effizient zu bestimmen, ob große Räume in voxelbasierten 3D-Räumen versiegelt sind. Ich bin an einem Punkt angelangt, an dem ich mein Bestes gegeben habe, um das Problem zu lösen, ohne um Hilfe zu bitten, aber nicht genug versucht habe, um aufzugeben, also bitte ich um Hilfe.
Zur Verdeutlichung versiegelt, dass es keine Löcher im Raum gibt. Es gibt Sauerstoffversiegelungen, die prüfen, ob der Raum versiegelt ist, und die je nach Sauerstoffzufuhr abdichten.
Im Moment mache ich das so:
- Beginnen Sie am Block über der Versiegelungskachel (die Entlüftung befindet sich auf der Oberseite der Versiegelung) und durchlaufen Sie rekursiv alle 6 benachbarten Richtungen
- Wenn es sich bei der angrenzenden Kachel um eine vollständige Kachel ohne Vakuum handelt, fahren Sie mit der Schleife fort
- Wenn die benachbarte Kachel nicht voll ist oder eine Vakuumkachel ist, überprüfen Sie rekursiv, ob es sich um benachbarte Blöcke handelt.
- Verringern Sie jedes Mal, wenn eine Kachel überprüft wird, einen Zähler
- Wenn der Zähler Null erreicht und der letzte Block neben einer Vakuumkachel liegt, geben Sie zurück, dass der Bereich nicht versiegelt ist
- Wenn der Zähler Null erreicht und der letzte Block keine Vakuumkachel ist oder die rekursive Schleife endet (keine Vakuumkacheln mehr vorhanden), bevor der Zähler Null ist, wird der Bereich versiegelt
Wenn der Bereich nicht versiegelt ist, führen Sie die Schleife mit einigen Änderungen erneut aus:
- Überprüfen benachbarter Blöcke auf "atmungsaktive Luft" -Fliesen anstelle einer Vakuumfliese
- Anstatt einen Dekrementierungszähler zu verwenden, fahren Sie fort, bis keine benachbarten "Atemluft" -Kacheln mehr gefunden werden.
- Sobald die Schleife beendet ist, setzen Sie jeden geprüften Block auf eine Vakuumkachel.
Hier ist der Code, den ich verwende: http://pastebin.com/NimyKncC
Das Problem:
Ich führe diese Prüfung alle 3 Sekunden durch. Manchmal muss ein Versiegeler Hunderte von Blöcken durchlaufen, und in einer großen Welt mit vielen Sauerstoffversiegelungen können diese mehreren rekursiven Schleifen alle paar Sekunden die CPU sehr belasten.
Ich habe mich gefragt, ob jemand mit mehr Erfahrung in der Optimierung mir helfen oder mich zumindest in die richtige Richtung weisen kann. Vielen Dank.
Antworten:
Die beste Lösung hängt von mehreren Faktoren ab, wie der erwarteten Raumgröße.
Ansatz 1:
Sie können ein A * verwenden, um einen Pfad von der Entlüftung zur Kachel über der Entlüftung / der Entlüftung selbst oder zu einer Kachel zu finden, die als Vakuum bezeichnet wird. Wenn ein Pfad gefunden wird, ist der Raum nicht versiegelt. Dies unterscheidet sich nicht so sehr von Ihrem aktuellen Ansatz, sollte aber schneller sein. Sobald gefunden, machen Sie eine "Flutfüllung", um die Fliesen als Vakuum einzustellen.
Ansatz 2:
Vielleicht ist Ihre äußere Struktur weniger vollständig - wenn man bedenkt, dass es eine Oberfläche gibt, unter der sich die Räume befinden, müssen Sie sich nicht in alle 6 Richtungen bewegen. Sie sollten sich also entlang der Oberfläche bewegen und jede Fliese als Vakuum markieren, das Sie bewegen.
quelle
Stellen Sie bei Ihrer rekursiven Suche sicher, dass Sie nicht dasselbe Voxel mehrmals überprüfen? An der Art und Weise, wie Sie Ihren Algorithmus beschrieben haben, konnte ich nichts sagen, aber Sie sollten eine Art Flag haben, um anzuzeigen, ob Sie ein Voxel bereits rekursiv erweitert haben, damit Sie es nicht mehr als einmal tun.
Und wie Byte56 sagte, sollten Sie auch nur dann auf Lecks prüfen, wenn sich die Dinge ändern. Dies kann den Arbeitsaufwand erheblich minimieren, je nachdem, wie häufig Änderungen auftreten. Möglicherweise können Sie sogar Informationen zwischen aufeinanderfolgenden Aufrufen des Algorithmus zwischenspeichern, wodurch der Rechenaufwand nach dem ersten Aufruf trivialisiert wird.
Bearbeiten:
Ich habe mir einen Teil Ihres Codes angesehen. Anscheinend verwenden Sie eine LinkedList, um anzugeben, ob ein Voxel bereits überprüft wurde, wie in meinem ersten Absatz. Sie können bessere Ergebnisse erzielen, wenn Sie dafür etwas anderes als eine LinkedList verwenden. Vielleicht versuchen Sie es mit einem HashSet oder so? Dies sollte die Komplexität Ihrer Prüfmethode von O (n) auf O (1) reduzieren.
quelle