Rubik sıralama bir matris (aka torus bulmaca)


16

Bu fikri basittir: bir tamsayılar matrisi verildiğinde, Rubik tarzı hareketleri uygulayarak sıralayalım. Bu, tek bir satır veya sütun seçebileceğiniz ve öğelerini herhangi bir yönde döndürebileceğiniz anlamına gelir:

[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)

Bu nedenle, herhangi bir boyutta tamsayılar matrisi verildiğinde, öğelerini yalnızca bu Rubik tarzı dönüşümleri uygulayarak sıralayın. Bir matris

[bir11bir12bir13bir14bir21bir22bir23bir24bir31bir32bir33bir34]

öğeleri aşağıdaki kısıtlamaya uyduğu takdirde sıralanmış olarak kabul edilecektir:

bir11bir12bir13bir14bir21bir22bir23bir24bir31bir32bir33bir34

I / O

  • Girdi, tekrarlanan değerleri olmayan pozitif tamsayıların bir matrisi olacaktır.
  • Çıktı, onu sıralamak için gereken hareketler olacaktır. Bu bir kod golf meydan okuması olmadığından ve uzunluğu hakkında endişelenmenize gerek olmadığından, her hareket için önerilen format , taşınacak satır veya sütunun sayısının (0 dizinli) olduğu ve #[UDLR]buradaki tek bir karakter olduğu hareketin Yukarı / Aşağı (sütunlar için) veya Sol / Sağ (satırlar için) olup olmadığını belirten aralık. Bu , "1. sütunu yukarı taşı" anlamına gelir, ancak "1. satırı sağa taşı" anlamına gelir. Bir çözelti, bu gibi ifade edilebilir böylece hareketleri virgülle ayrılmış olacaktır: .#[UDLR]1U1R1R,1U,0L,2D

puanlama

Bir matrisi bu şekilde sıralamaya çalışmak, birçok olası hareket kombinasyonu olduğu için pahalı olabilir ve bunu sıralayabilen birçok olası hareket listesi de vardır, bu nedenle amaç N * 'yi sıralayan bir kod yazmaktır. Aşağıdaki N matrisleri. Skor, hatasız makul bir süre 1'de çözebileceğiniz en büyük N boyutu olacaktır (matrisin boyutu ne kadar büyük olursa, o kadar iyidir). Beraberlik durumunda, kravat kırıcı bulunan yolunuzdaki hareketlerin sayısı olacaktır (yol ne kadar kısa olursa o kadar iyidir).

Örnek: A kullanıcısı N = 5 için bir çözüm bulursa ve B N = 6 için bir çözüm bulursa, B her iki yolun uzunluğuna bakılmaksızın kazanır. Her ikisi de N = 6 için çözüm bulursa, ancak A'nın bulduğu çözeltinin 50 adımı ve B'nin çözeltisinin 60 adımı varsa, A kazanır.

Kodunuzun nasıl çalıştığına ilişkin açıklamalar büyük bir teşviktir ve lütfen bunları test edebilmemiz için bulunan çözümleri gönderin . Çözümler çok büyükse Pastebin veya benzeri araçları kullanabilirsiniz . Ayrıca, çözümlerinizi bulmak için kodunuz tarafından harcanan sürenin tahmini takdir edilecektir.

Test senaryoları

Aşağıdaki matrisler ( daha kopyalanabilir bir versiyon için Pastebin bağlantısı ), zaten sıralanmış matrislerden 10K rastgele, Rubik tarzı hareketlerle karıştırılarak oluşturulmuştur:

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

Düz Metin Test Durumları:

[[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]]

Hepsini çözerseniz lütfen daha fazlasını isteyin. :-) Ve kum havuzundayken bu zorluğu düzeltmeme yardımcı olan insanlara çok teşekkürler .


1 Makul bir süre: çözümünüzü test ederken sabrımızı zedelemeyen herhangi bir süre. TIO'nun yalnızca 60 saniye kod çalıştırdığını unutmayın, bu sınırın üzerindeki herhangi bir süre kodu kodları makinelerimizde test etmemizi sağlayacaktır. Örnek: Oldukça verimsiz algoritmam, 3x3 ve 4x4 dereceli matrisleri çözmek için birkaç milisaniye sürer, ancak 5x5 matrisle test ettim ve çözmek 317 saniye sürdü (5 milyondan fazla hareketle, bunu düşünürsek çok komik) çözülecek matris sadece 10K kez karıştırıldı ). Hareket sayısını 10K'dan az azaltmaya çalıştım, ancak kodu yürüttükten 30 dakika sonra teslim oldum.


1
Güzel meydan okuma! Ancak, birkaç istek / sorum var: 1) Test senaryolarını daha kopyala yapıştır dostu bir formatta verebilir misiniz? (macun gibi) 2) Zaman sınırı sırasının daha kesin bir tanımını verebilir misiniz? 3) Matrisin kare olduğu garanti ediliyor mu? (Test senaryoları bunu öneriyor, ancak tanım değil.)
Arnauld

@Arnauld 1) Ben varım. 2) Bir zaman sınırı belirlemek istemedim ve meydan okuma sanal alanda iken hiç kimse herhangi bir sınır önermedi. Birine ihtiyacınız varsa, 30 dakikayı makul bir sınır olarak kabul eder misiniz? 3) Evet, test matrisleri gösterilenlerdir ve daha fazlası gerekirse hepsi kare olacaktır.
Charlie

Bu meydan okuma için (uygulanması nispeten kolay) bir O (girdi boyutu) algoritması vardır, bu yüzden ilk bakışta göründüğü kadar ilginç değildir.
user202729

@ user202729 O zaman giriş boyutu ne olurdu O(input size)? 5x5'lik bir matris için O(25)? Bu son derece hızlı görünüyor, bu yüzden algoritmanızı veya uygulamanızı görmek isterim. DÜZENLEME: 'Şifreli' matrisi girdiğimizi ve hareketleri çıkardığımızı anlıyorsunuz, değil mi? Tam tersi değil.
Kevin Cruijssen

Yanıtlar:


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

Çevrimiçi deneyin!

Yorumlarda bahsettiğim Algoritmalar 2012, 5, 18-29'da yayınlanan bir makaleden Torus bulmaca çözümü algoritmasını uygulamak için hızlı bir girişim .

Girdi matrisini, yer sınırlamalı sayılar satırı olarak düzleştirilmiş biçimde kabul eder.

Burada da Python 2'de bir doğrulayıcı var . Giriş olarak iki satır alır: ana şifreyle aynı formdaki orijinal şifreli matris ve önerilen hareket sırası. Doğrulayıcının çıktısı, bu hareketlerin uygulanmasından kaynaklanan matristir.

açıklama

Algoritmanın ilk bölümünde, sonuncusu hariç tüm satırları sıralarız.

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

Şekil 2'de, yazarlar 8 olası paterni ve karşılık gelen hareket dizilerini sunmaktadır, ancak kodumda tüm vakalar sadece 5 desenle kaplanmıştır, böylece hayır. 1, 5 ve 6 kullanılmaz.

İkinci kısımda, son iki eleman dışındaki son sıra proc line, her biri iki üçgen dönüşten oluşan bir çizgi ( ) üzerinde "üç eleman dönüşü" gerçekleştirilerek sıralanır (makalenin Şekil 3'üne bakınız).

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

nnTW=R,proc swapTW

n=21R,

Güncelleme: Dahaproc rotations az adımla sonuçlanacaksa hareketlerin yönünü tersine çeviren yeni eklendi .


Etkileyici! O zaman biraz daha test senaryosu oluşturacağım. Bu arada, bu çözümün optimize edilebileceğinden eminim, çünkü 7L,7L,7L,7L,7D,7D,7D,7Dazaltılabilecek ve 9x9 matris için 8R,8R,8R,8R,8R,8R,8Rdönüştürülebilecek gibi parçalar var 8L,8L.
Charlie

Algoritmanızı 100x100 matrisle denedim ve 4 saniyeden kısa sürede çözdü. Gerçekten bu sorunun doğrusal-zaman çözümü olmasını beklemiyordum. Gelecekte daha iyi zorluklar yazmaya çalışacağım!
Charlie

Belki de bu sorunu tek bir test matrisi olarak tek bir sabit matrisle ortaya koymak daha iyi olurdu ve kazanan kriteri daha önce bu problemin O olduğunu bilseydim (n ^ 2) çözeltisi. Bu cevabı yeni bir soruya böyle bir kazanan kriterle taşımayı düşünür müsünüz?
Charlie

@Charlie Halen mevcut çözümü biraz daraltmaya çalışırken, genel yol optimizasyonu probleminin nasıl çözüleceğine dair hiçbir fikrim yok ...
Kirill L.

5

Python 2 , TIO'da <30 saniyede 100 boyut

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

Çevrimiçi deneyin! Link, tam hareket çıkışı olan üç küçük test durumu ve kodun çalıştığını göstermek için 100x100 sessiz test içerir (taşıma çıkışı TIO sınırlarını aşacaktır). Açıklama: Kod dizi üzerinde ekleme sıralaması gerçekleştirmeye çalışır ve gittikçe artan sırada oluşturur. Son satır dışındaki tüm satırlar için birkaç durum vardır:

  • Öğe doğru sırada, ancak sütun 0'a ait. Bu durumda, sütun 0'a ulaşıncaya kadar döndürülür.
  • Öğe doğru yerde. Bu durumda, hiçbir şey olmuyor. (Bu, öğe 0 sütununa aitse de geçerlidir, bu durumda sadece 0 dönüş gerçekleşir.)
  • Öğe üst sırada, ancak yanlış sütunda. Bu durumda, öğe doğru sütuna oturana kadar aşağı, sonra yatay olarak döndürülür, sonra tekrar döndürülür.
  • Öğe doğru sırada, ancak yanlış sütunda. Bu durumda, yukarı döndürülür, daha sonra satır sütununa döndürülür, sonra aşağı döndürülür, daha sonra satır geri döndürülür. (Etkili olarak bu bir sonraki durumun dönüşüdür.)
  • Öğe doğru sütunda, ancak yanlış sırada. Bu durumda, satır son duruma düşürmek için sağa döndürülür.
  • Öğe yanlış satırda ve yanlış sütunda. Bu durumda, doğru sütun yanlış satıra (üst sıra için atlanır) döndürülür, daha sonra bu satır doğru sütuna döndürülür ve sonra sütun geri döndürülür.

Yukarıdaki rotasyonlar, adım sayısını en aza indiren hangi yönde yapılır; yukarıdaki açıklamaya bakılmaksızın, boyut 2 kare her zaman sola ve yukarı hareketlerle çözülür.

Alt sıra tamamlanmadan önce, toplam mesafeyi en aza indirmek için döndürülür, ancak aynı zamanda alt sıranın paritesinin, algoritmanın son kısmı tarafından değiştirilemediğinden emin olmak için döndürülür. Aynı minimum mesafeye sahip birden fazla rotasyon varsa, en az sayıda harekete sahip rotasyon seçilir.

Alt sıra için algoritma, elemanları üç sütunda değiştiren 7 işlem dizisine dayanır. Dizi, alt sıradaki geri kalan sayıların her birine, sırayla, arzu ettikleri konuma getirmek için uygulanır; mümkünse bu konumdaki eleman istenen konuma taşınır, ancak düz bir takas gerekiyorsa eleman basitçe mevcut en yakın sütuna taşınır ve umarım bir dahaki sefere düzeltilmesine izin verir.


Cevabınız için çok teşekkür ederim Neil! Ama unutmayın, bu bir kod golf değil. Kodun uzunluğu yerine, çözdüğünüz NxN matrisinin en büyük N boyutunu ve bu matrisi çözmek için hareket listesinin uzunluğunu belirtmelisiniz.
Charlie

@Charlie Eh, bu 6, ama sadece daha büyük bir matrise yapıştırmak için çok tembel olduğum için. Kaba kuvvet olmasına rağmen, alanla doğrusal olarak ölçeklenir, bu nedenle büyük matrisler yapabilmelidir.
Neil

Aslında, en kötü durum alanla ikinci dereceden olabilir.
Neil

1
@Charlie TIO bağlantısı artık rastgele bir 100x100 matrisi çözüyor.
Neil

1
@Charlie Şimdi alt sıra için büyük bir optimizasyon buldum, ama bu cevapta yapacağım son değişiklik olduğunu düşünüyorum.
Neil
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.