Permutasi dari Puzzle Lima Belas


13

Tantangan

Pertimbangkan diagram Lima Belas Teka-teki berikut dalam keadaan terselesaikan:

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

Pada setiap gerakan, seorang kusut yang bersemangat memiliki kesempatan untuk memindahkan satu bagian yang berdekatan dengan ruang kosong ke ruang kosong. Misalnya, setelah 1pindah, kami memiliki 2skenario yang memungkinkan (biarkan 0ruang kosong):

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

Setelah 2bergerak, puzzle memiliki 5hasil yang berbeda (Perhatikan bahwa kedua kasus di atas dikecualikan, karena mereka tidak dapat dicapai dalam 2 gerakan). Salah satu situasi ini adalah keadaan terselesaikan yang asli, dan dapat dicapai dengan dua cara berbeda.

Tugas Anda dalam tantangan ini adalah untuk menghasilkan sejumlah hasil yang berbeda yang dapat menyebabkan sejumlah gerakan tertentu. Sebagai input, ambil nomor N >= 0, dan hasilkan jumlah situasi unik yang mungkin muncul setelah Nbergerak.

Aturan

  • Ini adalah kode-golf. Kode terpendek menang!
  • Celah standar tidak diijinkan.
  • Kode Anda harus dapat menghitung kasing N = 10dalam beberapa menit. Saya kemungkinan tidak akan menguji aturan ini kecuali ada penyalahgunaan waktu yang jelas dalam jawaban.

Uji Kasus

(Hasil yang dihasilkan dari penjumlahan OEIS A089484 (Seperti yang dijelaskan Geobits dalam obrolan ), diotomatisasi oleh skrip Martin Büttner . Terima kasih atas semua bantuannya!)

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

Jawaban:


5

Pyth, 36 byte

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

Demonstrasi . Uji harness.

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 byte

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

Cobalah online di juru bahasa CJam (harus memakan waktu kurang dari 10 detik).

Bagaimana itu bekerja

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 byte

^
,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

Mengambil input dan mencetak output secara unary.

Anda dapat menempatkan setiap baris dalam satu file atau menjalankan kode seperti pada -sbendera. Misalnya:

> echo -n 111|retina -s fifteen_puzzle
111111111111

Inti dari metode ini adalah bahwa kami melacak semua posisi yang mungkin (tanpa pengulangan) yang dapat terjadi setelah klangkah yang tepat . Kami mulai formulir k = 0dan ulangi langkah substitusi (menggunakan (` and )` modifiers) sampai kami mencapai jumlah input langkah.

Selama perhitungan ini string kami selalu memiliki bentuk

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

di mana puzzle_stateadalah abcd%efgh%ijkl%mnoxdengan beberapa permutasi dari huruf. xsingkatan tempat kosong, sisa surat adalah ubin. %Adalah pembatas baris.

ymenandai bahwa negara dihasilkan pada langkah saat ini ( k) sehingga tidak boleh digunakan untuk menghasilkan negara lain di langkah ini.

1Tandai jumlah langkah yang tersisa.

Mekanika dasar kode Retina adalah bahwa setiap kecocokan dari garis ganjil diubah ke baris (datar) berikutnya.

Kode dengan penjelasan tambahan:

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 byte disimpan berkat @MartinButtner.


2

Python, 310 253 243 229 byte

Versi terbaru dengan peningkatan yang disarankan oleh @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)

Versi saya sendiri, yang lebih panjang (243 byte), tetapi lebih mudah dibaca:

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)

Sederhana pencarian pertama, encoding negara sebagai tuple, dan menyimpannya dalam satu set agar tetap unik.

Memakan waktu sekitar 0,03 detik pada laptop saya untuk N = 10. Waktu berjalan memang meningkat secara substansial untuk angka yang lebih besar, misalnya sekitar 12 detik untuk N = 20.


Mengasingkan s.addmungkin akan menyimpan beberapa karakter.
isaacg

@isaacg Saya menyimpan sedikit dengan memindahkan kode yang sama ke suatu fungsi. Melihat ini sekarang, saya mungkin tidak perlu tberselisih pendapat. Selain itu, saya pikir kemungkinan besar ada lebih banyak ruang untuk perbaikan jika saya memiliki keterampilan Python yang lebih baik.
Reto Koradi

3
Anda dapat mengkonversi ifpernyataan dalam ekspresi hubungan arus pendek dengan efek samping seperti j%4and e(j-1,j)sehingga Anda dapat menempatkan mereka ke dalam satu baris sebagai tupel boolean: 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 Kedengarannya bagus, saya akan mencobanya besok. Saya pikir mungkin ada beberapa cara cerdas untuk menggunakan ekspresi kondisional daripada serangkaian ifpernyataan. Saya juga bertanya-tanya apakah ada cara yang lebih pendek untuk membangun tuple dengan dua elemen yang ditukar.
Reto Koradi

1
Konversi ke daftar, swapping dan mengkonversi kembali ke tupel sedikit lebih pendek: 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

Contoh:

$ time perl 15.pl <<<20
2244884
real    0m39.660s
user    0m38.822s
sys 0m0.336s
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.