Rubik sắp xếp một ma trận (hay còn gọi là câu đố hình xuyến)


16

Ý tưởng cho này rất đơn giản: đưa ra một ma trận các số nguyên, hãy sắp xếp nó bằng cách áp dụng các chuyển động theo kiểu Rubik. Điều này có nghĩa là bạn có thể chọn một hàng hoặc cột đơn và xoay các phần tử của nó theo bất kỳ hướng nào:

[1, 3, 2, 4]  => [3, 2, 4, 1] (rotate left for rows/up for columns)
[1, 3, 2, 4]  => [4, 1, 3, 2] (rotate right for rows/down for columns)

Vì vậy, được đưa ra một ma trận các số nguyên của bất kỳ thứ nguyên nào, sắp xếp các phần tử của nó chỉ áp dụng các phép biến đổi kiểu Rubik này. Một ma trận

[a11a12a13a14a21a22a23a24a31a32a33a34]

sẽ được coi là sắp xếp nếu các yếu tố của nó tuân thủ các hạn chế sau:

a11a12a13a14a21a22a23a24a31a32a33a34

Tôi / O

  • Đầu vào sẽ là một ma trận các số nguyên dương không có giá trị lặp lại.
  • Đầu ra sẽ là các phong trào cần thiết để sắp xếp nó. Vì đây không phải là một thách thức golf mã và bạn không cần phải lo lắng về chiều dài của nó, định dạng đề xuất cho mỗi phong trào là #[UDLR]nơi #là số hàng hoặc cột để di chuyển (0-lập chỉ mục) và [UDLR]là một nhân vật duy nhất trong đó phạm vi chỉ định nếu chuyển động là Lên / Xuống (đối với cột) hoặc Trái / Phải (đối với hàng). Vì vậy, 1Ucó nghĩa là "di chuyển cột thứ 1 lên trên" nhưng 1Rsẽ là "di chuyển hàng thứ 1 sang phải". Các chuyển động sẽ được phân tách bằng dấu phẩy, vì vậy một giải pháp sẽ được thể hiện như sau : 1R,1U,0L,2D.

Chấm điểm

Cố gắng sắp xếp một ma trận theo cách này có thể tốn kém vì có rất nhiều sự kết hợp có thể di chuyển, và cũng có rất nhiều danh sách các động thái có thể sắp xếp nó, vì vậy mục tiêu là viết một số mã sắp xếp N * N ma trận dưới đây. Điểm số sẽ là kích thước N lớn nhất mà bạn có thể giải quyết trong khoảng thời gian hợp lý 1 mà không có lỗi (kích thước của ma trận được giải quyết càng lớn thì càng tốt). Trong trường hợp hòa, bộ ngắt kết nối sẽ là số lượng chuyển động trong đường dẫn tìm thấy của bạn (đường dẫn càng ngắn thì càng tốt).

Ví dụ: nếu người dùng A tìm thấy giải pháp cho N = 5 và B tìm thấy giải pháp cho N = 6, B sẽ thắng bất kể độ dài của cả hai đường dẫn. Nếu cả hai đều tìm giải pháp cho N = 6 nhưng giải pháp mà A tìm thấy có 50 bước và giải pháp của B có 60 bước thì A thắng.

Giải thích về cách hoạt động của mã của bạn rất được khuyến khích và vui lòng đăng các giải pháp được tìm thấy để chúng tôi có thể kiểm tra chúng . Bạn có thể sử dụng Pastebin hoặc các công cụ tương tự nếu các giải pháp quá lớn. Ngoài ra, ước tính thời gian sử dụng mã của bạn để tìm giải pháp của bạn sẽ được đánh giá cao.

Các trường hợp thử nghiệm

Các ma trận sau ( liên kết Pastebin cho phiên bản dễ sao chép hơn) đã được tạo bắt đầu từ các ma trận đã được sắp xếp bằng cách xáo trộn chúng với các chuyển động ngẫu nhiên, kiểu Rubik 10K:

[8561110131513]
[211012161762214851926132431]
[11381659402126221124143928321937310301736734]
[34214022354118333130124319113924282344136538451417916132683476254]
[20361711550187267341032355424396306139284154272357048132512465863523784533146859655673606422]
[85565275894441682715879132373973676419997846164221631004172131197309328403365070258058960845496172943342335776182482943866]
[567990617112211031551144284851306188443386611324962010275685888098351007713216410810601144023472731068232120263653936910454191111176217278873349155811695112571189151426545]

Các trường hợp kiểm tra bản rõ:

[[8, 5, 6], [11, 10, 1], [3, 15, 13]]

[[21, 10, 12, 16], [17, 6, 22, 14], [8, 5, 19, 26], [13, 24, 3, 1]]

[[1, 13, 8, 16, 5], [9, 40, 21, 26, 22], [11, 24, 14, 39, 28], [32, 19, 37, 3, 10], [30, 17, 36, 7, 34]]

[[34, 21, 40, 22, 35, 41], [18, 33, 31, 30, 12, 43], [19, 11, 39, 24, 28, 23], [44, 1, 36, 5, 38, 45], [14, 17, 9, 16, 13, 26], [8, 3, 47, 6, 25, 4]]

[[20, 36, 17, 1, 15, 50, 18], [72, 67, 34, 10, 32, 3, 55], [42, 43, 9, 6, 30, 61, 39], [28, 41, 54, 27, 23, 5, 70], [48, 13, 25, 12, 46, 58, 63], [52, 37, 8, 45, 33, 14, 68], [59, 65, 56, 73, 60, 64, 22]]

[[85, 56, 52, 75, 89, 44, 41, 68], [27, 15, 87, 91, 32, 37, 39, 73], [6, 7, 64, 19, 99, 78, 46, 16], [42, 21, 63, 100, 4, 1, 72, 13], [11, 97, 30, 93, 28, 40, 3, 36], [50, 70, 25, 80, 58, 9, 60, 84], [54, 96, 17, 29, 43, 34, 23, 35], [77, 61, 82, 48, 2, 94, 38, 66]]

[[56, 79, 90, 61, 71, 122, 110, 31, 55], [11, 44, 28, 4, 85, 1, 30, 6, 18], [84, 43, 38, 66, 113, 24, 96, 20, 102], [75, 68, 5, 88, 80, 98, 35, 100, 77], [13, 21, 64, 108, 10, 60, 114, 40, 23], [47, 2, 73, 106, 82, 32, 120, 26, 36], [53, 93, 69, 104, 54, 19, 111, 117, 62], [17, 27, 8, 87, 33, 49, 15, 58, 116], [95, 112, 57, 118, 91, 51, 42, 65, 45]]

Xin hỏi thêm nếu bạn giải quyết tất cả. :-) Và cảm ơn những người đã giúp tôi tinh chỉnh thử thách này khi ở trong hộp cát .


1 Lượng thời gian hợp lý: bất kỳ khoảng thời gian nào không làm giảm sự kiên nhẫn của chúng tôi trong khi thử nghiệm giải pháp của bạn. Lưu ý rằng TIO chỉ chạy mã trong 60 giây, bất kỳ khoảng thời gian nào vượt quá giới hạn đó sẽ khiến chúng tôi kiểm tra mã trong máy của mình. Ví dụ: thuật toán khá kém hiệu quả của tôi mất vài mili giây để giải các ma trận của thứ tự 3x3 và 4x4, nhưng tôi vừa thử nghiệm nó với ma trận 5x5 và phải mất tới 3,8 giây để giải quyết nó (trong hơn 5 triệu chuyển động, rất buồn cười nếu chúng ta xem xét điều đó ma trận để giải quyết được tranh giành chỉ 10K lần). Tôi đã cố gắng giảm số lượng chuyển động xuống dưới 10K nhưng tôi đã đầu hàng sau 30 phút thực thi mã.


1
Thử thách tốt đẹp! Tuy nhiên, tôi có một vài yêu cầu / câu hỏi: 1) Bạn có thể cung cấp các trường hợp thử nghiệm ở định dạng thân thiện với bản sao hơn không? (chẳng hạn như pastebin) 2) Bạn có thể cung cấp một định nghĩa chính xác hơn về thứ tự giới hạn thời gian không? 3) Ma trận có được đảm bảo là hình vuông không? (Các trường hợp thử nghiệm cho thấy như vậy, nhưng định nghĩa thì không.)
Arnauld

@Arnauld 1) Tôi đang ở trên đó. 2) Tôi không muốn thiết lập giới hạn thời gian và không ai đề xuất bất kỳ giới hạn nào trong khi thử thách nằm trong hộp cát. Nếu bạn cần một, bạn sẽ xem xét 30 phút một giới hạn hợp lý? 3) Có, các ma trận thử nghiệm là các ma trận được hiển thị và tất cả chúng sẽ là hình vuông nếu cần nhiều hơn.
Charlie

Tồn tại một thuật toán O (kích thước đầu vào) tương đối dễ thực hiện cho thách thức này, vì vậy nó không thú vị như lúc đầu.
dùng202729

@ user202729 Kích thước đầu vào của bạn O(input size)sẽ là bao nhiêu? Đối với ma trận 5x5 thì O(25)sao? Điều đó dường như là cực kỳ nhanh chóng, vì vậy tôi sẽ rất thích thú khi thấy thuật toán hoặc triển khai của bạn. EDIT: Bạn có nhận ra chúng ta nhập ma trận 'xáo trộn' và xuất các chuyển động, phải không? Không phải hướng ngược lại.
Kevin Cruijssen

4
Tôi nghĩ, nó giống như thuật toán này
Kirill L.

Câu trả lời:


8

Nim

import algorithm, math, sequtils, strutils

let l = split(stdin.readLine())
var m = map(l, parseInt)
let n = int(sqrt(float(len(m))))
let o = sorted(m, system.cmp[int])

proc rotations(P, Q: int): tuple[D, L, R, U: string, P, Q: int]=
  result = (D: "D", L: "L", R: "R", U: "U", P: P, Q: Q)
  if P > n - P:
    result.D = "U"
    result.U = "D"
    result.P = n - P
  if Q > n - Q:
    result.L = "R"
    result.R = "L"
    result.Q = n - Q

proc triangle(r: int): string=
  let p = r div n
  let q = r mod n
  let i = find(m, o[r])
  let s = i div n
  let t = i mod n
  var u = s
  var v = q
  if s == p and t == q:
    return ""
  var patt = 0
  if p == s: 
    u = s + 1
    patt = 4
  elif q == t:
    if q == n - 1:
      v = t - 1
      patt = 8
    else:
      u = p
      v = t + 1
      patt = 3
  elif t > q:
    patt = 2
  else:
    patt = 7
  var Q = abs(max([q, t, v]) - min([q, t, v]))
  var P = abs(max([p, s, u]) - min([p, s, u]))
  let x = p*n + q
  let y = s*n + t
  let z = u*n + v
  let w = m[x]
  m[x] = m[y]
  m[y] = m[z]
  m[z] = w
  let R = rotations(P, Q)

  result = case patt:
    of 2:
      repeat("$#$#," % [$v, R.D], R.P) & 
        repeat("$#$#," % [$u, R.L], R.Q) &
        repeat("$#$#," % [$v, R.U], R.P) & 
        repeat("$#$#," % [$u, R.R], R.Q)
    of 3:
      repeat("$#$#," % [$q, R.U], R.P) & 
        repeat("$#$#," % [$p, R.L], R.Q) &
        repeat("$#$#," % [$q, R.D], R.P) & 
        repeat("$#$#," % [$p, R.R], R.Q)
    of 4:
      repeat("$#$#," % [$p, R.L], R.Q) & 
        repeat("$#$#," % [$q, R.U], R.P) &
        repeat("$#$#," % [$p, R.R], R.Q) & 
        repeat("$#$#," % [$q, R.D], R.P)
    of 7:
      repeat("$#$#," % [$v, R.D], R.P) & 
        repeat("$#$#," % [$u, R.R], R.Q) &
        repeat("$#$#," % [$v, R.U], R.P) & 
        repeat("$#$#," % [$u, R.L], R.Q)
    of 8:
      repeat("$#$#," % [$s, R.R], R.Q) & 
        repeat("$#$#," % [$t, R.D], R.P) &
        repeat("$#$#," % [$s, R.L], R.Q) & 
        repeat("$#$#," % [$t, R.U], R.P)
    else: ""

proc Tw(p, t, P, Q: int): string =
  let S = P + Q
  result = "$#D,$#$#U,$#$#D,$#$#U," % [
    $t, if P > n - P: repeat("$#L," % $p, n - P) else: repeat("$#R," % $p, P),
    $t, if S > n - S: repeat("$#R," % $p, n - S) else: repeat("$#L," % $p, S), 
    $t, if Q > n - Q: repeat("$#L," % $p, n - Q) else: repeat("$#R," % $p, Q), 
    $t]

proc line(r: int): string=
  let p = n - 1
  let q = r mod n
  let i = find(m, o[r])
  var t = i mod n
  if t == q: 
    return ""
  let j = t == n - 1
  var P = t - q
  let x = p*n + q
  let y = x + P
  let z = y + (if j: -1 else: 1)
  let w = m[x]
  m[x] = m[y]
  m[y] = m[z]
  m[z] = w
  if j:
    let R = rotations(1, P)
    result = "$#D,$#$#U,$#$#R,$#D,$#L,$#U," % [
      $t, repeat("$#$#," % [$p, R.R], R.Q), 
      $t, repeat("$#$#," % [$p, R.L], R.Q), 
      $p, $t, $p, $t]
  else:
    result = Tw(p, t, P, 1)  
  
proc swap: string=
  result = ""
  if m[^1] != o[^1]:
    m = o
    for i in 0..(n div 2-1):
      result &= Tw(n - 1, n - 2*i - 1, 1, 1)
    result &= "$#R," % $(n - 1)
  
var moves = ""
for r in 0..(n^2 - n - 1):
  moves &= triangle(r)
if n == 2 and m[^1] != o[^1]:
  m = o
  moves &= "1R"
else:
  for r in (n^2 - n)..(n^2 - 3):
    moves &= line(r)
  if n mod 2 == 0:
    moves &= swap()
  if len(moves) > 0:
    moves = moves[0..^2]
  
echo moves

Hãy thử trực tuyến!

Một nỗ lực nhanh chóng để thực hiện thuật toán giải câu đố Torus từ một bài báo được xuất bản trong Thuật toán 2012, 5, 18-29 mà tôi đã đề cập trong các bình luận.

Chấp nhận ma trận đầu vào ở dạng dẹt, như một dòng các số được phân tách bằng dấu cách.

Đây cũng là một trình xác nhận trong Python 2 . Phải mất hai dòng làm đầu vào: ma trận được xáo trộn ban đầu ở cùng dạng với mã chính và chuỗi di chuyển được đề xuất. Đầu ra của trình xác nhận là ma trận kết quả từ việc áp dụng các di chuyển này.

Giải trình

Trong phần đầu tiên của thuật toán, chúng tôi sắp xếp tất cả các hàng trừ cái cuối cùng.

proc triangle[p,q][s,t][p,q][u,v]

Trong hình 2, các tác giả trình bày 8 mẫu có thể và các chuỗi di chuyển tương ứng, nhưng trong mã của tôi, tất cả các trường hợp thực sự chỉ được bao phủ bởi 5 mẫu, do đó không có. 1, 5 và 6 không được sử dụng.

Trong phần thứ hai, hàng cuối cùng ngoại trừ hai phần tử cuối cùng được sắp xếp bằng cách thực hiện "ba phần tử xoay" trên một dòng ( proc line), bao gồm hai phép quay tam giác mỗi phần (xem Hình 3 của bài viết).

[p,q][s,t][s,t+1]TWtt+1[s,t1]TW

nnTW=Rproc swapTW

n=21R

Cập nhật: Đã thêm mới proc rotationsđảo ngược hướng di chuyển nếu điều đó sẽ dẫn đến ít bước hơn.


Ấn tượng! Tôi sẽ tạo thêm một số trường hợp thử nghiệm sau đó. Trong khi đó, tôi chắc chắn rằng giải pháp này có thể được tối ưu hóa, vì có những phần giống như 7L,7L,7L,7L,7D,7D,7D,7Dcó thể được giảm bớt và 8R,8R,8R,8R,8R,8R,8Rcó thể được chuyển đổi thành 8L,8Lma trận 9x9.
Charlie

Tôi đã thử thuật toán của bạn với ma trận 100x100 và nó giải quyết nó trong chưa đầy 4 giây. Tôi thực sự không mong đợi vấn đề này có một giải pháp thời gian tuyến tính. Tôi sẽ cố gắng viết những thử thách tốt hơn trong tương lai!
Charlie

Có lẽ sẽ tốt hơn nếu đặt ra thử thách này với một ma trận cố định, duy nhất là trường hợp thử nghiệm duy nhất và đặt tiêu chí chiến thắng là kích thước của con đường tìm thấy để giải quyết nó, tôi đã biết trước rằng vấn đề này có O (n ^ 2) giải pháp. Bạn có cân nhắc chuyển câu trả lời này sang một câu hỏi mới với tiêu chí chiến thắng như vậy không?
Charlie

@Charlie Mặc dù tôi vẫn sẽ cố gắng tinh chỉnh giải pháp hiện tại một chút, tôi thực sự không biết làm thế nào để giải quyết vấn đề tối ưu hóa đường dẫn tổng thể ...
Kirill L.

5

Python 2 , kích thước 100 trong <30 giây trên TIO

import random
def f(a):
 d = len(a)
 r = []
 def V(j, b = -1):
  b %= d
  if d - b < b:
   for k in range(d - b):
    if r and r[-1] == "U%d" % j:r.pop()
    else:r.append("D%d" % j)
    b = a[-1][j]
    for i in range(len(a) - 1):
     a[-1 - i][j] = a[-2 - i][j]
    a[0][j] = b
  else:
   for k in range(b):
    if r and r[-1] == "D%d" % j:r.pop()
    else:r.append("U%d" % j)
    b = a[0][j]
    for i in range(len(a) - 1):
     a[i][j] = a[i + 1][j]
    a[-1][j] = b
 def H(i, b = -1):
  b %= d
  if d - b < b:
   for k in range(d - b):
    if r and r[-1] == "L%d" % i:r.pop()
    else:r.append("R%d" % i)
    a[i] = a[i][-1:] + a[i][:-1]
  else:
   for k in range(b):
    if r and r[-1] == "R%d" % i:r.pop()
    else:r.append("L%d" % i)
    a[i] = a[i][1:] + a[i][:1]
 b = sorted(sum(a, []))
 for i in range(d - 1):
  for j in range(d):
   c = b.pop(0)
   e = sum(a, []).index(c)
   if e / d == i:
    if j == 0:H(i, e - j)
    elif j < e % d:
     if i:
      V(e % d, 1)
      H(i, j - e)
      V(e % d)
      H(i, e - j)
     else:
      V(e)
      H(1, e - j)
      V(j, 1)
   else:
    if j == e % d:
     H(e / d)
     e += 1
     if e % d == 0:e -= d
    if i:
     V(j, i - e / d)
    H(e / d, e - j)
    V(j, e / d - i)
 c = [b.index(e) for e in a[-1]]
 c = [sum(c[(i + j) % d] < c[(i + k) % d] for j in range(d) for k in range(j)) % 2 and d * d or sum(abs(c[(i + j) % d] - j) for j in range(d)) for i in range(d)]
 e = min(~c[::-1].index(min(c)), c.index(min(c)), key = abs)
 H(d - 1, e)
 for j in range(d - 2):
  e = a[-1].index(b[j])
  if e > j:
   c = b.index(a[-1][j])
   if c == e:
    if e - j == 1:c = j + 2
    else:c = j + 1
   V(e)
   H(d - 1, j - e)
   V(e, 1)
   H(d - 1, c - j)
   V(e)
   H(d - 1, e - c)
   V(e, 1)
 return r

Hãy thử trực tuyến! Liên kết bao gồm ba trường hợp thử nghiệm nhỏ với đầu ra di chuyển đầy đủ, cộng với thử nghiệm im lặng 100x100 để cho thấy mã hoạt động (đầu ra di chuyển sẽ vượt quá giới hạn của TIO). Giải thích: Mã cố gắng thực hiện sắp xếp chèn trên mảng, xây dựng nó theo thứ tự tăng dần khi nó đi. Đối với tất cả các hàng ngoại trừ hàng cuối cùng, có một số trường hợp:

  • Phần tử nằm trong hàng đúng, nhưng thuộc về cột 0. Trong trường hợp này, nó chỉ được xoay cho đến khi đạt đến cột 0.
  • Các yếu tố là ở đúng nơi. Trong trường hợp này, không có gì xảy ra. (Điều này cũng đúng nếu phần tử thuộc cột 0, chỉ có 0 phép quay xảy ra trong trường hợp đó.)
  • Phần tử nằm ở hàng trên cùng nhưng ở cột sai. Trong trường hợp đó, nó được xoay xuống, sau đó theo chiều ngang cho đến khi phần tử nằm trong cột chính xác, sau đó được xoay lên một lần nữa.
  • Phần tử nằm trong hàng đúng nhưng trong cột sai. Trong trường hợp đó, nó được xoay lên, sau đó hàng được xoay vào cột của nó, sau đó nó được quay xuống, sau đó hàng được quay trở lại. (Thực tế đây là một vòng quay của trường hợp tiếp theo.)
  • Phần tử nằm trong cột đúng nhưng ở hàng sai. Trong trường hợp đó, hàng được xoay phải, để giảm nó đến trường hợp cuối cùng.
  • Phần tử nằm trong hàng sai và cột sai. Trong trường hợp này, cột đúng được xoay sang hàng sai (bỏ qua cho hàng trên cùng), hàng đó sau đó được xoay sang cột đúng và sau đó cột được quay trở lại.

Các phép quay trên được thực hiện theo hướng nào sẽ giảm thiểu số lượng bước; một hình vuông kích thước 2 luôn được giải quyết bằng cách di chuyển trái và lên, bất kể mô tả ở trên.

Trước khi hàng dưới cùng được hoàn thành, nó được xoay để giảm thiểu tổng khoảng cách ra khỏi vị trí, nhưng cũng để đảm bảo rằng tính chẵn lẻ của hàng dưới cùng là chẵn, vì phần cuối cùng của thuật toán không thể thay đổi. Nếu có nhiều hơn một vòng quay có cùng khoảng cách tối thiểu, vòng quay có số lần di chuyển nhỏ nhất sẽ được chọn.

Thuật toán cho hàng dưới cùng dựa trên chuỗi 7 thao tác trao đổi các phần tử trong ba cột. Trình tự được áp dụng cho từng số còn lại của hàng dưới cùng để lần lượt đưa chúng đến vị trí mong muốn; nếu có thể, phần tử ở vị trí đó được di chuyển đến vị trí mong muốn, nhưng nếu cần trao đổi thẳng, phần tử chỉ đơn giản được di chuyển đến cột có sẵn gần nhất, hy vọng cho phép nó được cố định vào lần tiếp theo.


Cảm ơn bạn rất nhiều vì câu trả lời của bạn, Neil! Nhưng hãy nhớ rằng, đây không phải là một mã golf. Thay vì độ dài của mã, bạn nên chỉ ra kích thước N lớn nhất của ma trận NxN mà bạn đã giải và độ dài của danh sách các chuyển động để giải ma trận đó.
Charlie

@Charlie Chà, đó là 6, nhưng chỉ vì tôi quá lười để dán vào một ma trận lớn hơn. Mặc dù đó là lực lượng vũ phu, nó có tỷ lệ tuyến tính với diện tích, do đó, nó phải có khả năng ma trận lớn.
Neil

Trên thực tế, trường hợp xấu nhất có thể là bậc hai với diện tích.
Neil

1
Liên kết TIO @Charlie hiện giải một ma trận 100x100 ngẫu nhiên.
Neil

1
@Charlie Bây giờ tôi đã đưa ra một tối ưu hóa lớn cho hàng dưới cùng, nhưng tôi nghĩ đó là thay đổi cuối cùng tôi sẽ thực hiện cho câu trả lời này.
Neil
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.