Yapboz Döndürme çözmek


14

Bazı eski Nokia telefonlarında, Rotation adlı on beş bulmacanın bir varyasyonu vardı . Bu varyasyonda, bir kerede bir döşemeyi kaydırmak yerine, bir seferde dört döşemeyi bir yönde döndürdünüz.

Bu oyunda, şöyle bir tahta ile başlarsınız:

4 9 2
3 5 7
8 1 6

Ve sol alt bloğu saat yönünde iki kez ve sol üst bloğu saat yönünde bir kez döndürerek şunu elde edersiniz:

4 9 2
8 3 7
1 5 6

4 9 2
1 8 7
3 5 6

1 4 2
8 9 7
3 5 6

ve 1döşeme olması gereken sol üst köşede olurdu. Sonunda, birkaç hamleden sonra aşağıdakilerle sonuçlanırsınız:

1 2 3
4 5 6
7 8 9

"orijinal" yapılandırmadır.

Göreviniz, giriş olarak 1'den 9'a kadar 3x3'lük bir sayı ızgarası (seçtiğiniz herhangi bir formatta) girecek ve tahtaya orijinaline geri döndürmek için yapmanız gereken hareketleri temsil eden bir dizi hamle çıktı olarak geri döndürecek bir program oluşturmaktır. yapılandırma (yine, seçtiğiniz herhangi bir biçimde). Yasal hamleler 4 karonun [üst / alt] - [sol / sağ] bloğunu [saat yönünde / saat yönünün tersine] hareket ettirmek olarak tanımlanır.

Programınız tüm olası 3x3 ızgaraları çözebilmelidir (tüm permütasyonlar çözülebilir).

Bunu yapmak için en kısa kod kazanır.


...and return as output a sequence of moves representing the moves you must take to return the board back to its originalBu "geri 1 2 3\n4 5 6\n7 8 9" demek mi? Bunu nasıl okuyacağımdan emin değilim.
yeraltı

Evet, demek istediğim 1 2 3 4 5 6 7 8 9.
Joe Z.

1
Örneğinizdeki ikinci ve üçüncü tahtanın 3 ve 5'i değiştirmesi gerektiğini düşünüyorum.
Martin Ender

@JoeZ. Çözümün en kötü durum performansının sınırlı olması gerektiğini bildirmek için değiştirilmesini öneririm.
HostileFork,

Yanıtlar:


7

GolfScript, 39/83 bayt

# Optimized for size:

{.4rand.p.2/+>`{?1420344440`=}+$..$>}do

# Optimized for speed:

6,(7++:t;~{.(1=.@7=9=+4\-rand+..2/+@.@>:s^[3s=0s=2s=4s=1s=]+s|.)9<\t>|}do.$>30764`*

Hız vs boyut

Boyuta göre optimize edilmiş versiyon, istenen permütasyon elde edilene kadar rastgele saat yönünde dönüşleri seçer. Bu yeterli, çünkü saat yönünün tersine dönüş, aynı karenin saat yönünde art arda üç dönüşüne eşdeğerdir.

Hızı optimize edilmiş sürüm aşağıdakiler dışında aynı şeyi yapar:

  1. 1 sayısı sol üst köşedeyse, artık sol üst kareyi döndürmez.

  2. 9 sayısı sağ alt köşedeyse, artık sağ alt kareyi döndürmez.

  3. Konum 7 ve 8'i değiştirme adımları sabit kodlanmıştır, bu nedenle ilmeğin kırılmasına izin veren iki konum vardır.

Algoritmayı değiştirmenin yanı sıra, hızı optimize edilmiş sürüm, dönüşü basit bir şekilde gerçekleştirirken, boyutu optimize edilmiş sürüm, GolfScript'in yerleşik sıralamasını eşleyerek kullanır. Ayrıca her yinelemede durumu sıralamak yerine son durumu (karşılaştırma için) kodlar.

Hızı optimize edilmiş sürüm daha az yineleme gerektirir ve her yineleme kendi başına çok daha hızlıdır.

Deneyler

Ben sayıların konumlarını rasgele ve test çalıştırılacak sürüme karşılık gelen çizgi uncommenting, test çalışmalarını gerçekleştirmek için aşağıdaki kodu kullandım:

[{[
    0:c;10,1>{;2 32?rand}$
    #{c):c;.4rand.2/+>`{?1420344440`=}+$..$>}do
    #6,(7++:t;{.(1=.@7=9=+4\-rand+..2/+@.@>:s^[3s=0s=2s=4s=1s=]+s|.)9<\t>|}do.$>30764`*
],c+}\~*]

$.0='Min: '\+puts .-1='Max: '\+puts ..{+}*\,/'Avg: '\+puts .,2/='Med: '\+

Çıktı, sayıları sipariş etmek için attığı minimum ve maksimum adım sayısını, tüm çalışmaların ortalamasını ve medyanını ve saniye cinsinden geçen süreyi gösterir:

$ TIME='\n%e s' time golfscript rotation-test-size.gs <<< 100
Min: 4652
Max: 2187030
Avg: 346668
Med: 216888

21500.10 s
$
$ TIME='\n%e s' time golfscript rotation-test-speed.gs <<< 1000
Min: 26
Max: 23963
Avg: 3036
Med: 2150

202.62 s

Makinemde (Intel Core i7-3770), boyutu optimize edilmiş sürümün ortalama yürütme süresi 3,58 dakikaydı. Hızı optimize edilen sürümün ortalama yürütme süresi 0.20 saniyeydi. Böylece, hızı optimize edilmiş versiyon yaklaşık 1075 kat daha hızlıdır.

Hızı optimize edilmiş versiyon 114 kat daha az dönüş sağlar. Her dönüşün gerçekleştirilmesi, esas olarak durumun nasıl güncellendiğinden kaynaklanmaktadır.

I / O

Çıktı 3 bit sayıdan oluşur. MSB saat yönünün tersine dönüşler için ayarlanmış, orta bit ise alt kareler için ve LSB sağ kareler için ayarlanmıştır. Böylece, 0 (4) sol üst kare, 1 (5) sağ üst, 2 (6) sol alt ve 3 (7) sağ alt karedir.

Hızı optimize edilmiş sürüm tüm dönüşleri tek bir satıra yazdırır. Boyutu optimize edilmiş sürüm, her satıra bir dönüş ve ardından sayıların son konumunu yazdırır.

Hızı optimize edilmiş versiyon için, giriş değerlendirilirken 1 ile 9 arasındaki sayıları içeren bir dizi vermelidir. Boyutu optimize edilmiş sürüm için, girişin son satırsonu olmayan bir dize olması gerekir; değerlendirilmez.

Örnek çalıştırmalar:

$ echo -n '253169748' | golfscript rotation-size.gs
3
0
123456789
$ golfscript rotation-speed.gs <<< '[5 4 7 1 2 9 3 8 6]'
2210300121312212222212211121122211122221211111122211211222112230764

Boyuta göre optimize edilmiş kod

{               #
  .             # Duplicate the state.
  4rand         # Push a randomly chosen integers between 0 and 3.
  .p            # Print that integer.
  .2/+          # Add 1 to it if it is grater than one. Possible results: 0, 1, 3, 4
  >`            # Slice the state at the above index.
  {             # Push a code block doing the following:
    ?           # Get the index of the element of the iteration in the sliced state.
    1420344440` # Push the string "14020344440".
    =           # Retrieve the element at the position of the computed index.
  }+            # Concatenate the code block with the sliced state.
  $             # Sort the state according to the above code block. See below.
  ..$>          # Push two copies of the state, sort the second and compare the arrays.
}do             # If the state is not sorted, repeat the loop.

Durumun güncellenmesi aşağıdaki şekilde gerçekleştirilir:

Dönme 2, 1 eklendikten sonra 3 tamsayısını verir. Durum "123456789" ise, durumun dilimlenmesi "456789" verir.

“$” Öğesini çalıştırmadan hemen önce, yığının en üstteki öğeleri şunlardır:

[ 1 2 3 4 5 6 7 8 9 ] { [ 4 5 6 7 8 9 ] ? "1420344440" = }

“$”, Öğenin kendisini ittikten sonra dizinin sıralanacak her öğesi için bloğu bir kez yürütür.

“[4 5 6 7 8 9]” deki 1 indeksi -1'dir (mevcut değildir), bu nedenle "1420344440" ın son elemanı itilir. Bu, 48 karakterini verir. 0 karakterine karşılık gelen ASCII kodu. 2 ve 3 için, 48 de itilir.

4, 5, 6, 7, 8 ve 9 için itilen tamsayılar 49, 52, 50, 48, 51 ve 52'dir.

Ayıklamadan sonra, devletin ilk elemanı 48 veren elemanlardan biri olacaktır; Son olarak, bu tür bir verim olacaktır. Yerleşik sıralama genel olarak kararsızdır, ancak bu özel durumda kararlı olduğunu ampirik olarak doğruladım.

Sonuç, sol alt karenin saat yönünde dönmesine karşılık gelen “[1 2 3 7 4 6 8 5 9]” dir.

Hızı optimize edilmiş kod

6,(7++:t;       # Save [ 1 2 3 4 5 7 ] in variable “t” and discard it.
~               # Interpret the input string.
{               #
  :s            # Duplicate the current state.
  (1=           # Unshift the first element and push 1 if it is equal to 1 and 0 otherwise.
  .@            # Duplicate the boolean and rotate the unshifted array on top of it.
  7=9=          # Push 1 if the eighth element of “s” is equal to 9 and 0 otherwise.
  +4\-          # Add the booleans and subtract their sum from 4.
  rand          # Push a randomly chosen integers between 0 and the result from above.
  +.            # Add this integer to the first boolean and duplicate it for the output.
  .2/+          # Add 1 to the result if it is grater than one. Possible results: 0, 1, 3, 4
  @.            # Rotate the state on top of the stack and duplicate it.
  @>:s          # Slice the state at the integer from above and save the result in “s”.
  ^             # Compute the symmetric difference of state and sliced state.
  [             # Apply a clockwise rotation to the sliced array:
    3s=         # The fourth element becomes the first.
    0s=         # The first element becomes the second.
    2s=         # The third element remains the same.
    4s=         # The fifth element becomes the fourth.
    1s=         # The second element becomes the fifth.
  ]             # Collect the results into an array.
  +             # Concatenate with array of elements preceding the slice.
  s|            # Perform set union to add the remaining elements of “s”.
  .             # Duplicate the updated state.
  )9<           # Pop the last element; push 0 if it is equal to 9 and 1 otherwise.
  \t            # Swap the popped state on top and push [ 1 2 3 4 5 7 ].
  >             # Push 0 if the state begins with [ 1 2 3 4 5 6 ] and 1 otherwise.
  |             # Take the logical OR of the booleans.
}do             # If the resulting boolean is 1, repeat the loop.
.$              # Duplicate the state and sort it.
>30764`*        # If the state was not sorted, 7 and 8 are swapped, so push "30764".

3, 0, 7, 6 ve 4 rotasyonlarının, kalan yedi elemanın pozisyonlarını değiştirmeden 7 ve 8 konumlarındaki elemanları değiştirdiğini gözlemleyin.


Hız için optimize edildi mi? Bu Golfscript ...
Mayıʇǝɥʇuʎs

1
@Synthetica: Bununla birlikte, şimdiye kadar yayınlanan en hızlı çözüm.
Dennis

4

Numpy ile Python - 158

from numpy import*
A=input()
while any(A.flat>range(1,10)):i,j,k=random.randint(0,2,3);A[i:i+2,j:j+2]=rot90(A[i:i+2,j:j+2],1+2*k);print"tb"[i]+"lr"[j]+"wc"[k]

Girdi aşağıdaki biçimde olmalıdır:

array([[1,2,5],[4,3,6],[7,8,9]])

Her çıktı satırı aşağıdaki gibi okunacak trwveya gibi dizelerde kodlanmış bir harekettir blc:

  • t: üst
  • b: alt
  • l: ayrıldı
  • r: sağ
  • c: saat yönünde
  • w: saat yönünün tersine (widdershins)

Bu program, hedef yapılandırmaya ulaşılana kadar rasgele hareketler gerçekleştirir. Yaklaşık olarak, her hareketin 1/9 bağımsız bir olasılığı olduğu varsayımı altında! hedef konfigürasyona¹ vurmak için, bir çözümden önceki devir sayısı 9 ortalama ile (yani ortalama hamle sayısı) katlanarak dağıtılır! ≈ 3.6 · 10⁵. Bu kısa bir deney ile uyumludur (20 çalışma).

¹ 9! toplam yapılandırma sayısıdır.


2
Yani özünde bir çözüm elde edene kadar rastgele hareketler dener?
Joe Z.

Benim için çalışıyor. Bir çözüme ulaşılmadan önce beklenen rotasyon sayısıyla ilgilenmeme rağmen.
Joe Z.

@JoeZ .: Yayımdaki düzenlemeye bakın.
Wrzlprmft

Bu harika.
Kyle Kanos

4

C ++ en az hamle çözümü - önce genişlik (1847 karakter)

Biraz daha düşündükten sonra, bence bunu çok daha verimli ve daha mantıklı yaptım. Bu çözüm, kesinlikle bu golfü kazanmasa da, şu ana kadar tahtayı çözecek en kısa dönüş sayısını bulmaya çalışan tek çözümdür. Şimdiye kadar, attığım her rastgele tahtayı dokuz veya daha az hareketle çözüyor. Ayrıca sonuncumdan çok daha iyi bir performans sergiliyor ve umarım Dennis'in aşağıdaki yorumlarını ele alıyor.

Önceki çözümden en büyük değişiklik, ana geçmişi yönetim kurulu durumundan (BS), geçmişi belirli bir derinlikte (DKH) depolayan yeni bir sınıfa taşımaktı. Uygulama her hareket ettiğinde, daha önce değerlendirilip değerlendirilmediğini görmek için bu derinlikte ve tüm derinliklerde geçmişi kontrol eder, öyleyse tekrar kuyruğa eklenmeyecektir. Bu, sıradaki depolamayı önemli ölçüde azaltmış gibi görünüyor (tüm bu geçmişi yönetim kurulu durumundan kaldırarak) ve bu nedenle kodun belleğinin tükenmesini önlemek için yapmak zorunda olduğum aptal budamayı neredeyse azaltıyor. Ayrıca, kuyruğa kopyalanacak çok daha az şey olduğu için çok daha hızlı çalışır.

Şimdi, çeşitli yönetim kurulu devletlerinde basit bir genişlik araması. Ayrıca, ortaya çıktığı gibi, anahtar kümesini (şu anda her biri BS :: anahtarıyla kartın temel-9 temsili olarak hesaplanan taban-9'da sayı kümesi olarak saklanır) bir bit kümesine değiştirmek istiyorum 9! bit gereksiz görünüyor; gerçi test / geçiş için bit kümesindeki bit hesaplamak için kullanılmış olabilir "faktöriyel sayı sistemi" bir anahtar hesaplamak için nasıl buldum.

Yani, en yeni çözüm:

#include <iostream>
#include <list>
#include <set>
#include <vector>
using namespace std;
struct BS{
#define LPB(i) for(int*i=b;i-b<9;i++)
struct ROP{int t, d;};
typedef vector<ROP> SV;
typedef unsigned int KEY;
typedef set<KEY> KH;
BS(const int*d){const int*x=d;int*y=b;for(;x-d<9;x++,y++)*y=*x;}
BS(){LPB(i)*i=i-b+1;}
bool solved(){LPB(i)if(i-b+1!=*i)return 0;return 1;}
void rot(int t, int d){return rot((ROP){t,d});}
void rot(ROP r){rotb(r);s.push_back(r);}
bool undo(){if (s.empty())return false;ROP &u=s.back();u.d*=-1;rotb(u);s.pop_back();return true;}
SV &sol(){return s;}
KEY key(){KEY rv=0;LPB(i){rv*=9;rv+=*i-1;}return rv;}
int b[9];
SV s;
void rotb(ROP r){int c=r.t<2?r.t:r.t+1;int bi=(r.d>0?3:4)+c;const int*ri=r.d>0?(const int[]){0,1,4}:(const int[]){1,0,3};for(int i=0;i<3;i++)swap(b[bi],b[c+ri[i]]);}
};
ostream &operator<<(ostream &o, BS::ROP r){static const char *s[]={"tl","tr","bl","br"};o<<s[r.t]<<(r.d<0?"w":"c");return o;}
struct DKH{
~DKH(){for(HV::iterator i=h.begin();i<h.end();++i)if(*i!=NULL)delete *i;}
void add(int d,BS b){h.resize(d+1);if(h[d]==NULL)h[d]=new BS::KH();h[d]->insert(b.key());}
bool exists(BS &b){BS::KEY k=b.key();size_t d=min(b.sol().size(),h.size()-1);do if (h[d]->find(k)!=h[d]->end())return true;while(d--!=0);return false;}
typedef vector<BS::KH *> HV;HV h;
};
static bool solve(BS &b)
{
const BS::ROP v[8]={{0,-1},{0,1},{1,-1},{1,1},{2,-1},{2,1},{3,-1},{3,1}};
DKH h;h.add(0,b);
list<BS> q;q.push_back(b);
while (!q.empty())
{
BS qb=q.front();q.pop_front();
if (qb.solved()){b=qb;return true;}
int d=qb.sol().size()+1;
for (int m=0;m<8;++m){qb.rot(v[m]);if (!h.exists(qb)){h.add(d,qb);q.push_back(qb);}qb.undo();}
}
return false;
}
int main()
{
BS b((const int[]){4,9,2,3,5,7,8,1,6});
if (solve(b)){BS::SV s=b.sol();for(BS::SV::iterator i=s.begin();i!=s.end();++i)cout<<*i<<" ";cout<<endl;}
}

1
Kodunuz C yerine C ++ gibi görünüyor
user12205

@ace, gerçekten, düzeltildi.
DreamWarrior

Başka vaka kimseye de sorunları, ben tüm örneklerini değiştirmek zorunda ++ g ile bu derleme vardır int[]için const int[]ve bayrak set -fpermissive.
Dennis

@Dennis, Üzgünüm, iki farklı g ++ derleyicisi ile derledim ve hiçbiri akla görünmüyordu. Ama daha yeni, daha katı bir versiyonun nasıl sızlanacağını görebiliyorum. Teşekkürler.
DreamWarrior

Şimdi iyi derleniyor ve çok daha hızlı. Sorudan sildiğiniz yorumu ele almak için: 11 adım gerektiren bazı permütasyonlar vardır. 978654321 bunlardan biri.
Dennis

1

CJam - 39

l{4mr_o_1>+_@m<_[Z0Y4X]\f=\5>+m>__$>}g;

Başka bir rasgele çözücü :)
492357816 gibi bir dize alır ve her biri bir bloğun saat yönünde dönüşünü temsil eden 0 ila 3 arasında bir (uzun) basamak dizisi çıkarır: 0 = sol üst, 1 = sağ üst, 2 = alt -left, 3 = sağ alt.

Kısa açıklama:

4mr0'dan 3'e kadar rasgele bir sayı üretir
_1>+(1'den büyükse
m<sayıyı arttırır (böylece 0, 1, 3 veya 4 ile biteriz - 4 bloğun başlangıç ​​dizinleri) dizeyi sola döndürür (492357816 -> 923578164, blok döndürme değil), bloğu ilk konuma döndürmek için,
[Z0Y4X]\f=12345 -> 41352 gibi ilk 5 karakteri etkileyen blok döndürmeyi yapar;
X = 1, Y = 2, Z = 3 yani [Z0Y4X] aslında [3 0 2 4 1] ve bunlar döndürülen döşemelerin 0 tabanlı dizinleri
5>, dizenin geri kalanını kopyalar
m>(değiştirilmiş) dizeyi geri döndürür sağ
__$>dizenin sıralanıp sıralanmadığını kontrol eder (durma koşulu)


1

Mathematica, 104 karakter

Görevi permütasyon gruplarının dilinde yorumlayabiliriz. Dört rotasyonu simetrik grup S oluşturmak sadece dört permütasyon 9 ve görev sadece jeneratörün bir ürün olarak bir permütasyon yazmak. Mathematica'nın bunu yapmak için yerleşik bir işlevi vardır.

i={1,2,5,4};GroupElementToWord[PermutationGroup[Cycles/@({i}+#&/@i-1)],Input[]~FindPermutation~Range@9]

Misal:

Giriş:

{4, 9, 2, 8, 3, 7, 1, 5, 6}

Çıktı:

{-2, -3, -4, 2, 4, 1, 4, -1, -2, 3, 2, -4, 3, 4, -3, -3, -4, -4, -2, -2, -3, -2, 3, -1}
  • 1: sol üst saat yönünde
  • 2: sağ üst saat yönünde
  • 3: sağ alt saat yönünde
  • 4: sol alt saat yönünde
  • -1: saat yönünün tersine sol üst
  • -2: saat yönünün tersine sağ üst
  • -3: sağ alt saat yönünün tersine
  • -4: sol alt saat yönünün tersine
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.