Permutationen der fünfzehn Puzzle


13

Die Herausforderung

Betrachten Sie das folgende Diagramm des Fünfzehn-Puzzles im gelösten Zustand:

_____________________
|    |    |    |    |
| 1  | 2  | 3  | 4  |
|____|____|____|____|
|    |    |    |    |
| 5  | 6  | 7  | 8  |
|____|____|____|____|
|    |    |    |    |
| 9  | 10 | 11 | 12 |
|____|____|____|____|
|    |    |    |    |
| 13 | 14 | 15 |    |
|____|____|____|____|

Bei jeder Bewegung hat ein aufgeregter Puzzler die Möglichkeit, ein Teil neben der Leerstelle in die Leerstelle zu verschieben. Zum Beispiel 1haben wir nach dem Umzug 2mögliche Szenarien (lassen Sie 0ein Leerzeichen sein):

1   2   3   4          1   2   3   4
5   6   7   8          5   6   7   8
9   10  11  12   and   9   10  11  0
13  14  0   15         13  14  15  12

Nach 2Zügen hat das Rätsel 5unterschiedliche Ergebnisse (Beachten Sie, dass die beiden oben genannten Fälle ausgeschlossen sind, da sie in 2 Zügen nicht erreicht werden können). Eine dieser Situationen ist der ursprünglich gelöste Zustand und kann auf zwei verschiedene Arten erreicht werden.

Ihre Aufgabe bei dieser Herausforderung besteht darin, die Anzahl der unterschiedlichen Ergebnisse zu ermitteln, zu denen eine bestimmte Anzahl von Zügen führen kann. Nehmen Sie als Eingabe eine Zahl N >= 0und geben Sie die Anzahl der eindeutigen Situationen aus, die nach NZügen auftreten können.

Regeln

  • Das ist Code-Golf. Kürzester Code gewinnt!
  • Standardlücken sind nicht zulässig.
  • Ihr Code sollte in der Lage sein, den Fall N = 10innerhalb weniger Minuten zu berechnen . Ich werde diese Regel wahrscheinlich nicht testen, es sei denn, in einer Antwort liegt ein offensichtlicher Zeitmissbrauch vor.

Testfälle

(Ergebnisse generiert aus Summen von OEIS A089484 (Wie Geobits im Chat beschrieben ), automatisiert von Martin Büttner's Skript . Danke für all die Hilfe!)

0 moves: 1
1 moves: 2
2 moves: 5
3 moves: 12
4 moves: 29
5 moves: 66
6 moves: 136
7 moves: 278
8 moves: 582
9 moves: 1224
10 moves: 2530
11 moves: 5162
12 moves: 10338
13 moves: 20706
14 moves: 41159
15 moves: 81548
16 moves: 160159
17 moves: 313392
18 moves: 607501
19 moves: 1173136
20 moves: 2244884
21 moves: 4271406
22 moves: 8047295
23 moves: 15055186
24 moves: 27873613
25 moves: 51197332
26 moves: 93009236
27 moves: 167435388
28 moves: 297909255
29 moves: 524507316
30 moves: 911835416
31 moves: 1566529356

Antworten:


5

Pyth, 36 Bytes

lu{smmXd,0@dk)fq1.a.DR4,Txd0UdGQ]U16

Vorführung . Kabelbaum testen.

lu{smmXd,0@dk)fq1.a.DR4,Txd0UdGQ]U16

                 .a.DR4,Txd0            Find the Euclidean distance between the
                                        present location of 0 and a given location.
              fq1           Ud          Filter over all locations on that distance
                                        equaling 1.
     mXd,0@dk)                          Map each such location to the grid with 0
                                        and the value at that location swapped.
  {sm                         G         Map all unique grids possible after n-1
                                        steps to all unique grids after n steps.
 u                             Q]U16    Repeat <input> times, starting with the
                                        initial grid.
l                                       Print the length of the resulting set.

3

CJam, 54 52 51 50 49 47 45 Bytes

G,ari{{:S0#S{4md2$4md@-@@-mh1=},f{Se\}}%:|}*,

Probieren Sie es online im CJam-Interpreter aus (sollte weniger als 10 Sekunden dauern).

Wie es funktioniert

G,a       e# Push R := [[0 1 ... 15]].
ri{       e# Do int(input()) times:
  {:S     e#   For each S in R:
    0#    e#     Push the index of 0 in S (I).
    S{    e#     Filter S; for each J in in S:
      4md e#       Push J/4 and J%4.
      2$  e#       Copy I.
      4md e#       Push I/4 and I%4.
      @-  e#       Compute (I%4)-(J%4).
      @@- e#       Compute (J%4)-(I%4).
      mh  e#       2-norm distance: a b -> sqrt(aa + bb)
      1=  e#       Check if the distance is 1.
    },    e#     Keep all values of J with distance 1 from I.
    f{    e#     For each J:
      S   e#       Push S. 
      e\  e#       Swap S at indexes I and J.
    }     e#     This pushes an array of all valid modifications of S.
  }%      e#   Collect the results for all S in R in an array.
  :|      e#   Reduce the outmost array using set union (removes duplicates).
}*        e#

3

Retina , 289 276 Bytes

^
,abcd%efgh%ijkl%mnox,
(`(,[^,]*)x([^,%])([^,y]*),
$0$1$2x$3y,
(,[^,]*)([^,%])x([^,y]*),
$0$1x$2$3y,
(,[^,]*)x([^,]{4})([^,])([^,y]*),
$0$1$3$2x$4y,
(,[^,]*)([^,])([^,]{4})x([^,y]*),
$0$1x$3$2$4y,
,.{19},(?=.*1)|,[^,]{20},(?=[^1]*$)|y|1$

+)`([^,]{19})(.*),\1,
$1$2
[^a]

a
1

Übernimmt die Eingabe und druckt die Ausgabe in unary.

Sie können jede Zeile in eine einzelne Datei einfügen oder den Code so wie er ist mit dem -sFlag ausführen . Z.B:

> echo -n 111|retina -s fifteen_puzzle
111111111111

Der Kern der Methode besteht darin, dass wir alle möglichen Positionen (ohne Wiederholung) verfolgen, die nach genauen kSchritten auftreten können. Wir starten das Formular k = 0und wiederholen die Substitutionsschritte (mit der (` and )` modifiers), bis wir die eingegebene Anzahl von Schritten erreicht haben.

Während dieser Berechnung hat unsere Zeichenfolge immer die Form von

(,[puzzle_state]y?,)+1*

wo puzzle_stateist abcd%efgh%ijkl%mnoxmit etwas Permutation der Buchstaben. xsteht für den leeren Platz, der Rest der Buchstaben sind die Kacheln. %sind Zeilenbegrenzer.

ymarkiert, dass der Status im aktuellen Schritt generiert wird ( k), sodass er in diesem Schritt nicht zum Generieren anderer Status verwendet werden sollte.

1's markieren die Anzahl der verbleibenden Schritte.

Der grundlegende Mechanismus des Retina-Codes besteht darin, dass jede Übereinstimmung einer ungeraden Zeile in die nächste (gerade) Zeile geändert wird.

Der Code mit zusätzlicher Erklärung:

initialize string
^
,abcd%efgh%ijkl%mnox,

while string changes
(`

for every old (y-less) state concatenate a new state with moving the empty tile to r/l/d/u if possible
right
(,[^,]*)x([^,%])([^,y]*),
$0$1$2x$3y,
left
(,[^,]*)([^,%])x([^,y]*),
$0$1x$2$3y,
down
(,[^,]*)x([^,]{4})([^,])([^,y]*),
$0$1$3$2x$4y,
up
(,[^,]*)([^,])([^,]{4})x([^,y]*),
$0$1x$3$2$4y,

if we should have made this step (there are 1's left) remove old states
,.{19},(?=.*1)

if we should not have made this step (no more 1's left) remove new states
,[^,]{20},(?=[^1]*$)

remove y markers
y

remove one 1 (decrease remaining step count)
1$


remove duplicates until string changes (with + modifier)
+`([^,]{19})(.*),\1,
$1$2    

end while
)`

remove non-a's, 1 a stays from each state
[^a]

change a's to 1's
a
1

10 Bytes gespart dank @MartinButtner.


2

Python, 310 253 243 229 Bytes

Neueste Version mit Verbesserungsvorschlag von @randomra:

s=set()
s.add(tuple(range(16)))
def e(a,b):s.add(t[:a]+(t[b],)+t[a+1:b]+(t[a],)+t[b+1:])
for k in range(input()):
 p,s=s,set()
 for t in p:j=t.index(0);j%4and e(j-1,j);j%4>2or e(j,j+1);j<4or e(j-4,j);j>11or e(j,j+4)
print len(s)

Meine eigene Version, die länger war (243 Bytes), aber leichter zu lesen:

s=set()
s.add(tuple(range(16)))
def e(a,b):s.add(t[:a]+(t[b],)+t[a+1:b]+(t[a],)+t[b+1:])
for k in range(input()):
 p,s=s,set()
 for t in p:
  j=t.index(0)
  if j%4:e(j-1,j)
  if j%4<3:e(j,j+1)
  if j>3:e(j-4,j)
  if j<12:e(j,j+4)
print len(s)

Einfache Breitensuche, bei der die Zustände als Tupel codiert und in einer Gruppe gespeichert werden, um sie eindeutig zu halten.

Dauert auf meinem Laptop ungefähr 0,03 Sekunden für N = 10. Die Laufzeit erhöht sich bei größeren Zahlen erheblich, beispielsweise um 12 Sekunden bei N = 20.


Aliasing s.addwürde wahrscheinlich einige Zeichen speichern.
isaacg

@isaacg Ich habe einiges gespart, indem ich den ähnlichen Code in eine Funktion verschoben habe. Wenn ich mir das jetzt anschaue, muss ich wahrscheinlich kein tArgument vorbringen. Abgesehen davon gibt es wahrscheinlich mehr Raum für Verbesserungen, wenn ich bessere Python-Fähigkeiten hätte.
Reto Koradi

3
Sie können die konvertieren ifwie Anweisungen in Kurzschließen Ausdrücke mit Nebenwirkung , j%4and e(j-1,j)so dass Sie sie in eine Zeile als boolean Tupel setzen können: j%4and e(j-1,j),j%4>2or e(j,j+1),j<4or e(j-4,j),j>11or e(j,j+4).
Randomra

@ Randomra Klingt gut, ich werde das morgen versuchen. Ich dachte, es gäbe wahrscheinlich eine clevere Möglichkeit, bedingte Ausdrücke anstelle der Reihe von ifAnweisungen zu verwenden. Ich frage mich auch, ob es eine kürzere Möglichkeit gibt, ein Tupel mit zwei getauschten Elementen zu erstellen.
Reto Koradi

1
Die Umstellung auf die Liste, Tauschen und zurück zu Tupel Umwandlung ist etwas kürzer: def e(a,b):*l,=t;l[a],l[b]=l[b],l[a];s.add(tuple(l)).
Randomra

1

Perl, 148

#!perl -p
$s{"abcd.efgh.ijkl.mno#"}=1;for(1..$_){$x=$_,map{$r{$_}=1if
s/($x)/$3$2$1/}keys%s for
qw!\w)(# #)(\w \w)(.{4})(# #)(.{4})(\w!;%s=%r;%r=()}$_=keys%s

Beispiel:

$ time perl 15.pl <<<20
2244884
real    0m39.660s
user    0m38.822s
sys 0m0.336s
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.