Ich implementiere einen Algorithmus, der sehr rechenintensiv sein wird, und möchte versuchen, sicherzustellen, dass ich keine unnötige Arbeit mache.
Es gibt ein nxnxn-Kubikgitter, zB wenn n = 2, besteht dieses aus (0,0,0), (0,1,0), (1,0,0), (1,1,0), (0, 1,1), (0,0,1), (1,0,1), (1,1,1).
Aus diesem Gitter werde ich rekursiv alle Mengen von m Punkten erzeugen, so etwas wie:
solve(set_of_points) {
if set_of_points.size = m, finish
do some useful computation here
for each point in lattice not in set_of_points:
solve(set_of_points + new_point);
}
Dies kann dann beginnend mit einem leeren Satz von Punkten aufgerufen werden.
Das Problem ist so beschaffen, dass ich nicht jede Permutation von m Punkten benötige, sondern nur diejenigen, die unter natürlichen Symmetrien des Würfels einzigartig sind.
Nehmen Sie zum Beispiel einen 2x2x2-Würfel und nehmen wir an, wir wollen alle Sätze von 1 Punkt. Unter dem obigen Basisalgorithmus gibt es 8 verschiedene Sätze von 1 Punkt.
Mit den Symmetrien des Würfels können wir dies jedoch auf 1 eindeutigen Satz von 1 Punkten reduzieren, da alle ursprünglichen 8 unter den Symmetrien des Würfels äquivalent sind (in diesem Fall sind sie alle "Ecken").
Wenn der Würfel 2x2x2 und m = 2 ist, gibt es 28 Sätze im Basisalgorithmus, aber dies reduziert sich unter Symmetrie auf nur 3 (z. B. {(0,0,0), (1,0,0)}, {(0) , 0,0), (1,1,0)}, {(0,0,0), (1,1,1)})
Offensichtlich ist die Berechnung für 3 Punktmengen viel besser als 28, daher ist meine Frage, wie ich vorgehen soll, keine Punktmengen zu generieren, die symmetrisch einer bereits erzeugten Menge entsprechen. Oder wenn dies nicht möglich ist, wie kann ich zumindest die Anzahl der Sätze ein wenig reduzieren.
(Beachten Sie - wenn m = 1 ist dies relativ einfach - wählen Sie einfach die Punkte aus, die näher an (0,0,0) liegen als alle anderen Eckpunkte, wobei an den Grenzen ein wenig verwackelt wird. Dies gilt für m> 1 ein echtes Problem sein)
quelle
Antworten:
Basiskonzept:
(1) Wir können Punkt (0,0,0) einfach als 000 betrachten. Jeder Punkt im Gitter fällt nun in eine einfache Reihenfolge. Der erste Punkt ist 000, dann 001, dann 010 011 100 101 110 und 111. In dieser Reihenfolge werden Sie versuchen, sie zur Punktmenge hinzuzufügen.
(2) In ähnlicher Weise kann die Menge {(0,0,0), (0,0,1), (0,1,0)} einfach als 000001010 und die Menge {(0,0,0) angesehen werden. , (0,1,0), (0,0,1)} kann einfach als 000010001 angesehen werden. Zwei verschiedene Sätze können nicht dieselbe Reihenfolge haben, und Sie können 000001010 leicht als numerisch oder alphabetisch kleiner als 000010001 anzeigen. Nennen wir dies den Sollwert. Jeder mögliche Satz von N Punkten hat jetzt einen Sollwert, und alle möglichen Sätze von N Punkten fallen jetzt in eine einfache, gut geordnete Liste.
(3) Jede isomorphe Gruppe von Punktmengen hat genau ein Element, das den niedrigsten Mengenwert hat. Dies sind die einzigen, bei denen wir tatsächlich "nützliche Berechnungen" durchführen.
(4) Hier ist der Teil, der erhebliche Arbeit erfordert. Bevor Sie lösen ausführen (set_of_points + new_point), möchten Sie sehen, ob ein Isomorphismus den Sollwert für set_of_points + new_point senken würde. Wenn ein Isomorphismus den eingestellten Wert senken würde, ist dies KEIN Mitglied mit dem niedrigsten Wert der isomorphen Menge. Wir überspringen die Arbeit an diesem neuen Punkt. Wir überspringen auch alle rekursiven Arbeiten, die wir innerhalb dieser Lösung ausgeführt hätten (set_of_points, Kandidat_point).
quelle
unter der Notation der obigen Antwort.
Definieren wir zunächst die Symmertrie, die von der Funktion drehen vorgeschlagen wird (Richtung, Anzahl der Zeiten).
Lösung:
(1) Erstellen Sie einen Hash aller Mengen der Permutation mit jeweils Flag = 0. zum Beispiel für n = 2, m = 2 000 001 = 0 000 010 = 0 000 011 = 0 ect '...
(2) Beginnen Sie mit dem Init-Set, zum Beispiel i = 000,001
(3) Drehen Sie die Menge i mit der Drehfunktion (oder einer anderen Symmetrie, die Sie mögen) in alle Richtungen. Beispielsweise sollte die Drehfunktion für jede Permutation der Drehung 24-mal aufgerufen werden.
Explenation: Jede Zahl 1-6 kann vor Ihnen liegen und jede Zahl kann viermal gedreht werden, daher 6 * 4 = 24
(4) Setzen Sie für jeden Satz, der aus der Kombination entfernt wurde, das Hash-Flag auf 1 (es hat bereits einen symmetrischen Satz).
(5) Aktualisiere i auf den nächsten Satz, zum Beispiel i = 000,010
(6) Wenn die Menge i im Hash bereits markiert ist, gehe zu (5), andernfalls gehe zu (3).
Wir sind fertig, wenn der gesamte Hash als 1 markiert ist.
quelle
Hinweis: Ich denke hier nur an Spiegelsymmetrien, nicht an Rotationssymmetrien.
Nehmen wir an, wir haben einen (Hyper-) Würfel mit d Dimensionen, jede n Einheit lang (ein Zauberwürfel wäre d = 3, n = 3 ).
Ein naiver Algorithmus würde n ^ d Kombinationen von Punkten erzeugen und jede auf einen Symmetriekonflikt mit allen anderen prüfen.
Wenn wir eine Kombination von Punkten als einen Bitvektor mit einer Länge von n ^ d Bits darstellen, können wir eine Abbildung (Bitvektor -> Boolescher Wert) verwenden, um alle Symmetrien des Bitvektors mit true zu markieren . Wir könnten dann eine Kombination überspringen, wenn sie bereits in der Karte markiert ist.
Dieser Ansatz ist sehr platzsparend: Es wird eine Karte mit 2 ^ (n ^ d) Einträgen benötigt, dh eine Bitmap mit so vielen Bits. (Für Rubiks Würfel wären es 2 ^ 27 = 128 MBit = 16 MByte.)
Wir können uns nur an die kanonischen Darstellungen erinnern, dh an solche Bitvektoren, die den kleinsten ganzzahligen Wert haben, wenn sie als vorzeichenloses n ^ d- Bit-Wort dargestellt werden. Wenn wir eine neue Permutation von Punkten erzeugen, erzeugen wir alle ihre Symmetrien und prüfen nur, ob wir die Symmetrie mit dem kleinsten numerischen Wert gesehen haben. Auf diese Weise können wir eine Karte mit nur 2 ^ n Bits speichern (nur 1 Byte für den Zauberwürfel), da wir 2 ^ d- Symmetrien haben. Es erzeugt jedoch diese 2 ^ d- Symmetrien bei jedem Schritt, so dass wir O (2 ^ (d ^ n + d)) = O (2 ^ (d ^ n) * 2 ^ d) Zeit verbringen . Immer noch arm.
Wir können die Idee aus dem vorherigen Absatz auf den eindimensionalen Fall anwenden. Um alle Kombinationen in einem Vektor der Länge d zu erzeugen , können wir einfach eine Binärzahl d Bits lang inkrementieren , beginnend mit allen
0
s. Teilen wir unseren Vektor in zwei d / 2- lange Segmente, z. B. links und rechts. Wir können feststellen, dass1
wir für jedes Bit im linken Segment nur Kombinationen sehen müssen, bei denen sich das1
Bit in der symmetrischen Position des rechten Abschnitts befindet. Andernfalls hätten wir bereits früher eine symmetrische Kombination erzeugt, als die Positionen der Bits vertauscht wurden und die0
vor dem kamen1
. Auf diese Weise für jede Bitposition in der rechten Hälfte (r) und die symmetrische Position in der linken Hälfte(l) wir müssen nur 3 Kombinationen erzeugen: (l = 0, r = 0); (l = 1, r = 1); (l = 1, r = 0) . Wir müssen also nur 2 ^ (d / 2) -Permutationen eines Vektors der Länge d erzeugen , was 3 Kombinationen für jede Permutation ergibt.Ein Würfel mit d Dimensionen kann aus n ^ (d-1) Vektoren konstruiert werden. Der obige Trick gibt uns die Vektoren billiger als der naive Ansatz. Um einen Würfel zu erzeugen, benötigen wir O (n ^ (d-1) * 2 ^ (d / 2)) Zeit.
Wenn wir den Würfel entlang der Dimension unserer eindimensionalen Vektoren betrachten, können wir sehen, dass wir entlang dieser Dimension nicht auf Symmetrie prüfen müssen: Während wir die Würfel erzeugen, eliminieren wir Symmetrien für jeden beteiligten Vektor.
Wenn wir nun über diese Dimension schauen , können wir denselben Trick wiederverwenden.
Wenn wir hinüberblicken, betrachten wir z. B. die ersten Bits von Vektoren, die eine bestimmte Ebene bilden. Diese Bits repräsentieren einen eindimensionalen Bitvektor. Wir können die meisten Kombinationen seiner Bits aus Symmetriegründen eliminieren, wie oben beschrieben. Wenn wir also einen bestimmten 1-d-Vektor eines Würfels auswählen (z. B. ganz oben links), können wir viele Vektoren derselben Ebene (z. B. oben) basierend auf dem Wert eines bestimmten Bits eliminieren. Für einen Vektor in einer spiegelsymmetrischen Position in der Ebene können wir also vermeiden, dass bei allen Kombinationen dieses Bit gesetzt (oder nicht gesetzt) wird, wodurch die Anzahl der Vektoren, die für eine bestimmte Ebene erzeugt werden müssen, drastisch reduziert wird. Jedes eliminierte Bit halbiert die Anzahl möglicher Vektoren an der spiegelreflektierten Position. Dies gibt uns eine Folge von Ebenen ohne symmetrische Gegenstücke entlang jeder Dimension.
Dieser Trick kann angewendet werden, um die Erzeugung von Permutationen der folgenden Ebenen entlang einer dritten Dimension usw. weiter zu begrenzen.
Obwohl dies kein vollständiger Algorithmus ist, hoffe ich, dass dies hilft.
quelle