Python 1166 Bytes
Aus Gründen der Lesbarkeit wurde eine beträchtliche Menge an Leerzeichen gelassen. Die Größe wird nach dem Entfernen dieser Leerzeichen gemessen und das Ändern von verschiedenen Einrückungen zu Tab
, Tab
Space
, Tab
Tab
etc. Ich habe auch alle Golf vermieden , das die Leistung zu stark beeinträchtigt.
T=[]
S=[0]*20,'QTRXadbhEIFJUVZYeijf',0
I='FBRLUD'
G=[(~i%8,i/8-4)for i in map(ord,'ouf|/[bPcU`Dkqbx-Y:(+=P4cyrh=I;-(:R6')]
R=range
def M(o,s,p):
z=~p/2%-3;k=1
for i,j in G[p::6]:i*=k;j*=k;o[i],o[j]=o[j]-z,o[i]+z;s[i],s[j]=s[j],s[i];k=-k
N=lambda p:sum([i<<i for i in R(4)for j in R(i)if p[j]<p[i]])
def H(i,t,s,n=0,d=()):
if i>4:n=N(s[2-i::2]+s[7+i::2])*84+N(s[i&1::2])*6+divmod(N(s[8:]),24)[i&1]
elif i>3:
for j in s:l='UZifVYje'.find(j);t[l]=i;d+=(l-4,)[l<4:];n-=~i<<i;i+=l<4
n+=N([t[j]^t[d[3]]for j in d])
elif i>1:
for j in s:n+=n+[j<'K',j in'QRab'][i&1]
for j in t[13*i:][:11]:n+=j%(2+i)-n*~i
return n
def P(i,m,t,s,l=''):
for j in~-i,i:
if T[j][H(j,t,s)]<m:return
if~m<0:print l;return t,s
for p in R(6):
u=t[:];v=s[:]
for n in 1,2,3:
M(u,v,p);r=p<n%2*i or P(i,m+1,u,v,l+I[p]+`n`)
if r>1:return r
s=raw_input().split()
o=[-(p[-1]in'UD')or p[0]in'RL'or p[1]in'UD'for p in s]
s=[chr(64+sum(1<<I.find(a)for a in x))for x in s]
for i in R(7):
m=0;C={};T+=C,;x=[S]
for j,k,d in x:
h=H(i,j,k)
for p in R(C.get(h,6)):
C[h]=d;u=j[:];v=list(k)
for n in i,0,i:M(u,v,p);x+=[(u[:],v[:],d-1)]*(p|1>n)
if~i&1:
while[]>d:d=P(i,m,o,s);m-=1
o,s=d
Beispielnutzung:
$ more in.dat
RU LF UB DR DL BL UL FU BD RF BR FD LDF LBD FUL RFD UFR RDB UBL RBU
$ pypy rubiks.py < in.dat
F3R1U3D3B1
F2R1F2R3F2U1R1L1
R2U3F2U3F2U1R2U3R2U1
F2L2B2R2U2L2D2L2F2
Dies ist eine Implementierung des Thistlethwaite-Algorithmus, bei der für jeden Schritt eine IDA * -Suche verwendet wird. Da alle heuristischen Tabellen im laufenden Betrieb berechnet werden müssen, wurden mehrere Kompromisse eingegangen, wobei eine Heuristik normalerweise in zwei oder mehr Teile gleicher Größe aufgeteilt wurde. Dies beschleunigt die Berechnung der heuristischen Tabellen um ein Vielfaches und verlangsamt die Suchphase, normalerweise nur geringfügig. Dies kann jedoch in Abhängigkeit vom ursprünglichen Cube-Status erheblich sein.
Variablenindex
T
- die heuristische Haupttabelle.
S
- ein gelöster Würfelzustand. Jedes einzelne Stück wird als Bitmaske gespeichert und als Zeichen dargestellt. Ein gelöster Orientierungsvektor ist als der Nullvektor definiert.
I
- die verschiedenen Wendungen in der Reihenfolge, in der sie aus dem Suchfeld entfernt werden.
G
- die Gruppen für Twist-Permutationen, die als auszutauschende Paare gespeichert sind. Jedes Byte in der komprimierten Zeichenfolge codiert für ein Paar. Für jede Drehung sind sechs Tauschvorgänge erforderlich: drei für den Kantenzyklus und drei für den Eckenzyklus. Die komprimierte Zeichenfolge enthält nur druckbare ASCII-Zeichen (Zeichen 32 bis 126).
M
- eine Funktion, die einen Zug ausführt, gegeben von G.
N
- wandelt eine Permutation von vier Objekten zu Kodierungszwecken in eine Zahl um.
H
- berechnet den heuristischen Wert für den angegebenen Würfelstatus, der zum Nachschlagen der Verschiebungstiefe von T verwendet wird.
P
- Führen Sie eine Suche in einer einzelnen Tiefe einer einzelnen Phase des Algorithmus durch.
s
- Der Permutationsstatus des Eingabewürfels.
o
- Der Orientierungsvektor des Eingabewürfels.
Performance
Unter Verwendung des Datensatzes von Tomas Rokicki ergab dieses Skript einen Durchschnitt von 16,02 Drehungen pro Lösung (maximal 35) mit einer durchschnittlichen Zeit von 472 ms (i5-3330 CPU @ 3,0 Ghz, PyPy 1.9.0). Die minimale Lösungszeit betrug 233 ms mit einem Maximum von 2,97 s, Standardabweichung 0,488. Nach den Bewertungsrichtlinien des Wettbewerbs (Leerraum wird nicht gezählt, Schlüsselwörter und Bezeichner zählen bei einer Länge von 870 als ein Byte) hätte die Gesamtpunktzahl 13.549 betragen.
In den letzten 46 Fällen (den Zufallszuständen) wurden durchschnittlich 30,83 Drehungen pro Lösung mit einer durchschnittlichen Zeit von 721 ms ermittelt.
Anmerkungen zum Thistlethwaite-Algorithmus
Hier finden Sie eine kurze Erklärung für alle, die eine Implementierung des Thistlethwaite-Algorithmus versuchen möchten .
Der Algorithmus arbeitet nach einem sehr einfachen Lösungsprinzip zur Raumreduzierung. Das heißt, reduzieren Sie den Würfel auf einen Zustand, in dem eine Teilmenge von Drehungen nicht erforderlich ist, um ihn zu lösen, reduzieren Sie ihn auf einen kleineren Lösungsraum und lösen Sie den Rest mit nur den wenigen verbleibenden Drehungen.
Thistlethwaite schlug ursprünglich vor <L,R,F,B,U,D>
→ <L,R,F,B,U2,D2>
→ <L,R,F2,B2,U2,D2>
→ <L2,R2,F2,B2,U2,D2>
. Angesichts des Eingabeformats halte ich es jedoch für einfacher, zuerst auf <L,R,F2,B2,U,D>
(keine Vierteldrehung F
oder B
) zu reduzieren und dann, <L2,R2,F2,B2,U,D>
bevor schließlich der halbe Drehungszustand erreicht wird. Anstatt genau zu erklären, warum dies der Fall ist, wird es meiner Meinung nach offensichtlich, nachdem die Kriterien für jeden Staat definiert wurden.
<L,R,F,B,U,D>
⇒ <L,R,F2,B2,U,D>
Um zu beseitigen F
und B
Vierteldrehungen, nur die Kanten müssen korrekt ausgerichtet sein. Gilles Roux hat auf seiner Website eine sehr gute Erklärung dafür, was "richtige" und "falsche" Orientierung ist, also überlasse ich die Erklärung ihm. Aber im Grunde (und das ist , warum dieses Eingabeformat so günstig ist F
und B
Beseitigung), wird eine Kante Cubie richtig ausgerichtet , wenn sie die folgende Regex übereinstimmt: [^RL][^UD]
. Eine korrekte Ausrichtung wird in der Regel mit 0
und falsch mit gekennzeichnet 1
. Grundsätzlich können U
und D
Aufkleber möglicherweise nicht auf den R
oder L
Flächen oder an den Kanten der beliebigen U
oder D
Kantenwürfel erscheinen, oder sie können nicht an Ort und Stelle verschoben werden, ohne dass ein F
oder erforderlich istB
Vierteldrehung.
<L,R,F2,B2,U,D>
⇒ <L2,R2,F2,B2,U,D>
Zwei Kriterien hier. Zunächst müssen alle Ecken richtig ausgerichtet werden, und die zweite, die jeweils die für die Mittelschicht Cubies ( FR
, FL
, BR
, BL
) muss irgendwo in der Mittelschicht sein. Eine Eckausrichtung ist aufgrund des Eingabeformats sehr einfach zu definieren: die Position des ersten U
oder D
. Hat zum Beispiel URB
Orientierung 0
(richtig orientiert), LDF
hat Orientierung 1
und LFU
hat Orientierung 2
.
<L2,R2,F2,B2,U,D>
⇒ <L2,R2,F2,B2,U2,D2>
Hier gelten folgende Kriterien: Jedes Gesicht darf nur Aufkleber von seinem Gesicht oder von dem Gesicht direkt gegenüber enthalten. Zum Beispiel kann es auf dem U
Gesicht nur U
und D
Aufkleber geben, auf dem R
Gesicht nur R
und L
Aufkleber geben, auf dem F
Gesicht nur F
und B
Aufkleber usw. Der einfachste Weg, dies sicherzustellen, besteht darin, zu überprüfen, ob sich jedes Kantenstück in befindet seine "Scheibe" und jedes Eckstück in seiner "Umlaufbahn". Zusätzlich muss auf die Kanten-Ecken-Parität geachtet werden. Wenn Sie jedoch nur auf Eckparität prüfen, ist auch die Kantenparität garantiert und umgekehrt.
Wie Drehungen die Orientierung beeinflussen
U
und D
Verdrehungen wirken sich weder auf die Kantenausrichtung noch auf die Eckenausrichtung aus. Die Teile können direkt getauscht werden, ohne den Ausrichtungsvektor zu aktualisieren.
R
und L
Drehungen wirken sich nicht auf die Kantenausrichtung aus, wirken sich jedoch auf die Eckenausrichtung aus. Je nachdem, wie Sie Ihren Zyklus definieren, ist die Änderung der Eckenausrichtung entweder +1, +2, +1, +2
oder vollständig +2, +1, +2, +1
modulo 3
. Es ist zu beachten, dass R2
und L2
Drehungen die Eckenorientierung nicht beeinflussen, da +1+2
Null-Modulo ist 3
, wie es ist +2+1
.
F
und B
wirken sich sowohl auf die Kantenorientierungen als auch auf die Eckenorientierungen aus. Kantenorientierungen werden zu +1, +1, +1, +1
(Mod 2), und Eckenorientierungen sind dieselben wie für R
und L
. Beachten Sie dies F2
und B2
beeinflussen Sie weder die Kantenausrichtungen noch die Eckenausrichtungen.