เรียงสับเปลี่ยนของสิบห้าปริศนา


13

ความท้าทาย

พิจารณาแผนภาพต่อไปนี้ของ Fifteen Puzzle ในสถานะที่แก้ไขแล้ว:

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

ทุกครั้งที่มีการเคลื่อนไหวปริศนาอันน่าตื่นเต้นจะมีโอกาสเคลื่อนย้ายชิ้นส่วนหนึ่งที่อยู่ติดกับพื้นที่ว่างไปยังพื้นที่ว่าง ตัวอย่างเช่นหลังจาก1ย้ายเรามี2สถานการณ์ที่เป็นไปได้ (ปล่อยให้0เป็นพื้นที่ว่าง):

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

หลังจาก2ย้ายแล้ว Puzzle จะมี5ผลลัพธ์ต่างกัน (โปรดทราบว่าทั้งสองกรณีข้างต้นได้รับการยกเว้นเนื่องจากไม่สามารถเข้าถึงได้ใน 2 การเคลื่อนไหว) หนึ่งในสถานการณ์เหล่านี้คือสถานะที่แก้ไขดั้งเดิมและสามารถเข้าถึงได้ในสองวิธีที่แตกต่างกัน

งานของคุณในความท้าทายนี้คือการผลิตจำนวนของผลลัพธ์ที่แตกต่างกันที่จำนวนหนึ่งของการเคลื่อนไหวที่จะนำไปสู่ เป็น input ใช้จำนวนN >= 0และการส่งออกจำนวนของที่ไม่ซ้ำกันสถานการณ์ที่อาจปรากฏขึ้นหลังNการเคลื่อนไหว

กฎระเบียบ

  • นี่คือรหัสกอล์ฟ รหัสที่สั้นที่สุดชนะ!
  • ช่องโหว่มาตรฐานไม่ได้รับอนุญาต
  • รหัสของคุณควรจะสามารถคำนวณเคสN = 10ในสองสามนาที ฉันจะไม่ทดสอบกฎนี้เว้นแต่ว่ามีการละเมิดเวลาอย่างเห็นได้ชัดในคำตอบ

กรณีทดสอบ

(ผลลัพธ์ที่สร้างขึ้นจากการสรุปของOEIS A089484 (ตามที่ Geobits อธิบายไว้ในการแชท ) โดยอัตโนมัติโดยสคริปต์ของ Martin Büttner ขอบคุณสำหรับความช่วยเหลือทั้งหมด!)

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

คำตอบ:


5

Pyth, 36 ไบต์

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

การสาธิต สายรัดทดสอบ

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 ไบต์

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

ลองออนไลน์ในล่าม CJam (ควรใช้เวลาน้อยกว่า 10 วินาที)

มันทำงานอย่างไร

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 ไบต์

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

ใช้อินพุตและพิมพ์เอาต์พุตใน unary

คุณสามารถใส่แต่ละบรรทัดในไฟล์เดียวหรือเรียกใช้รหัสเช่นเดียวกับ-sธง เช่น:

> echo -n 111|retina -s fifteen_puzzle
111111111111

หลักสำคัญของวิธีการคือเราติดตามตำแหน่งที่เป็นไปได้ทั้งหมด (โดยไม่มีการทำซ้ำ) ซึ่งสามารถเกิดขึ้นได้หลังจากkขั้นตอนที่แน่นอน เราเริ่มต้นรูปแบบk = 0และทำซ้ำขั้นตอนการทดแทน (ใช้(` and )` modifiers) จนกว่าเราจะถึงจำนวนของขั้นตอนการป้อนข้อมูล

ในระหว่างการคำนวณสตริงของเราจะมีรูปแบบของ

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

ที่puzzle_stateเป็นabcd%efgh%ijkl%mnoxที่มีการเปลี่ยนแปลงของตัวอักษรบาง xย่อมาจากสถานที่ที่ว่างส่วนที่เหลือของตัวอักษรเป็นกระเบื้อง %เป็นตัวคั่นแถว

yทำเครื่องหมายว่าสถานะถูกสร้างขึ้นในขั้นตอนปัจจุบัน ( k) ดังนั้นจึงไม่ควรใช้เพื่อสร้างสถานะอื่นในขั้นตอนนี้

1ทำเครื่องหมายจำนวนขั้นตอนที่เหลือ

กลไกพื้นฐานของรหัส Retina คือการจับคู่ทุกครั้งของเส้นคี่จะเปลี่ยนเป็นบรรทัดถัดไป (คู่)

รหัสพร้อมคำอธิบายเพิ่มเติม:

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 ไบต์ด้วย @MartinButtner


2

Python, 310 253 243 229 ไบต์

รุ่นล่าสุดพร้อมการปรับปรุงที่แนะนำโดย @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)

รุ่นของฉันเองซึ่งยาวกว่า (243 ไบต์) แต่อ่านง่ายกว่า:

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)

ค้นหาความกว้างอย่างง่ายแรกเข้ารหัสสถานะเป็นสิ่งอันดับและเก็บไว้ในชุดเพื่อให้มีความโดดเด่น

ใช้เวลาประมาณ 0.03 วินาทีบนแล็ปท็อปของฉันสำหรับ N = 10 เวลาทำงานจะเพิ่มขึ้นอย่างมีนัยสำคัญสำหรับตัวเลขขนาดใหญ่เช่นประมาณ 12 วินาทีสำหรับ N = 20


การใช้นามแฝงs.addอาจบันทึกอักขระบางตัว
isaacg

@isaacg ฉันบันทึกได้ค่อนข้างน้อยโดยการย้ายรหัสที่คล้ายกันลงในฟังก์ชั่น ดูที่ตอนนี้ฉันอาจไม่ต้องผ่านtการโต้แย้ง นอกจากนั้นฉันคิดว่ามีโอกาสมากขึ้นที่จะปรับปรุงถ้าฉันมีทักษะ Python ที่ดีขึ้น
Reto Koradi

3
คุณสามารถแปลงifงบลงไปในการแสดงออกไฟฟ้าลัดวงจรที่มีผลข้างเคียงเช่นj%4and e(j-1,j)เพื่อให้คุณสามารถใส่ไว้ในหนึ่งบรรทัดเป็น tuple 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 ฟังดูดีฉันจะลองวันพรุ่งนี้ ฉันคิดว่าอาจมีวิธีที่ชาญฉลาดในการใช้นิพจน์ที่มีเงื่อนไขแทนชุดifคำสั่ง ฉันยังสงสัยว่ามีวิธีที่สั้นกว่าในการสร้าง tuple ที่มีสององค์ประกอบสลับกันหรือไม่
Reto Koradi

1
การแปลงเป็นลิสต์การแลกเปลี่ยนและการแปลงกลับเป็นทูเปิลจะสั้นกว่าเล็กน้อย: 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

ตัวอย่าง:

$ time perl 15.pl <<<20
2244884
real    0m39.660s
user    0m38.822s
sys 0m0.336s
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.