W niektórych starych telefonach Nokia istniała odmiana piętnastu układanek o nazwie Rotation. W tej odmianie zamiast przesuwać jedną płytkę na raz, obróciłeś cztery płytki na raz w jednym kierunku.
W tej grze zaczynasz od takiej planszy:
4 9 2
3 5 7
8 1 6
A obracając lewy dolny blok dwa razy w prawo i lewy górny blok raz w prawo, uzyskasz:
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
a 1
płytka będzie w lewym górnym rogu, gdzie powinna być. W końcu po kilku kolejnych ruchach uzyskasz:
1 2 3
4 5 6
7 8 9
która jest „oryginalną” konfiguracją.
Twoim zadaniem jest zbudowanie programu, który pobierze jako dane wejściowe siatkę 3x3 liczb od 1 do 9 (w dowolnym wybranym przez Ciebie formacie) i zwróci jako wynik sekwencję ruchów reprezentujących ruchy, które musisz wykonać, aby przywrócić planszę do pierwotnego stanu konfiguracja (ponownie, w dowolnym wybranym formacie). Legalne ruchy są zdefiniowane jako przesunięcie bloku [góra / dół] - [lewo / prawo] z 4 płytek [zgodnie z ruchem wskazówek zegara / przeciwnie do ruchu wskazówek zegara].
Twój program musi być w stanie rozwiązać wszystkie możliwe siatki 3x3 (wszystkie kombinacje są rozwiązywalne).
Najkrótszy kod do tego celu wygrywa.
źródło
...and return as output a sequence of moves representing the moves you must take to return the board back to its original
Czy to oznacza „powrót do1 2 3\n4 5 6\n7 8 9
”? Nie jestem pewien, jak to przeczytać.Odpowiedzi:
GolfScript, 39/83 bajtów
Szybkość a rozmiar
Wersja zoptymalizowana pod względem wielkości losowo wybiera obroty zgodnie z ruchem wskazówek zegara, aż do uzyskania pożądanej permutacji. Jest to wystarczające, ponieważ obrót w kierunku przeciwnym do ruchu wskazówek zegara jest równoważny trzem kolejnym obrotom tego samego kwadratu zgodnie z ruchem wskazówek zegara.
Wersja zoptymalizowana pod kątem prędkości robi to samo, z wyjątkiem następujących czynności:
Jeśli liczba 1 znajduje się w lewym górnym rogu, nie obraca już lewego górnego kwadratu.
Jeśli liczba 9 znajduje się w prawym dolnym rogu, nie obraca już prawego dolnego kwadratu.
Kroki do zamiany pozycji 7 i 8 są zakodowane na stałe, więc są dwie pozycje, które pozwalają na przerwanie pętli.
Oprócz zmiany algorytmu, wersja zoptymalizowana pod kątem prędkości osiąga obrót w prosty sposób, podczas gdy wersja o zoptymalizowanym rozmiarze korzysta z wbudowanego sortowania GolfScript przez mapowanie. Określa również stan końcowy (dla porównania) zamiast sortować stan w każdej iteracji.
Wersja zoptymalizowana pod kątem prędkości wymaga mniej iteracji, a każda iteracja jest znacznie szybsza sama w sobie.
Benchmarki
Użyłem następującego kodu do randomizacji pozycji liczb i wykonania testów, odkomentowując wiersz odpowiadający testowanej wersji:
Dane wyjściowe pokazują minimalną i maksymalną liczbę kroków, które wykonano, aby uporządkować liczby, średnią i medianę wszystkich przebiegów, a także czas, jaki upłynął w sekundach:
Na moim komputerze (Intel Core i7-3770) średni czas wykonania wersji zoptymalizowanej pod względem wielkości wynosił 3,58 minuty. Średni czas wykonania wersji zoptymalizowanej pod kątem prędkości wynosił 0,20 sekundy. Dlatego wersja zoptymalizowana pod kątem prędkości jest około 1075 razy szybsza.
Wersja zoptymalizowana pod kątem prędkości zapewnia 114 razy mniej obrotów. Wykonywanie każdego obrotu jest 9,4 razy wolniejsze, co wynika głównie ze sposobu aktualizacji stanu.
I / O
Wyjście składa się z liczb 3-bitowych. MSB jest ustawiony na obroty przeciwnie do ruchu wskazówek zegara, środkowy bit jest ustawiony na dolne kwadraty, a LSB jest ustawiony na prawe kwadraty. Zatem 0 (4) jest lewym górnym kwadratem, 1 (5) prawym górnym, 2 (6) lewym dolnym i 3 (7) prawym dolnym.
Wersja o zoptymalizowanej prędkości drukuje wszystkie obroty w jednym wierszu. Wersja zoptymalizowana pod względem wielkości drukuje jeden obrót na linię, a następnie końcową pozycję liczb.
W przypadku wersji zoptymalizowanej pod kątem prędkości dane wejściowe muszą dać tablicę zawierającą liczby od 1 do 9, gdy zostaną ocenione. W przypadku wersji zoptymalizowanej pod względem wielkości wejściowym musi być ciąg bez końcowej nowej linii; nie podlega ocenie.
Przykładowe przebiegi:
Kod zoptymalizowany pod kątem rozmiaru
Aktualizacja stanu odbywa się w następujący sposób:
Obrót 2 daje liczbę całkowitą 3 po dodaniu 1. Jeśli stan to „123456789”, przecięcie stanu daje „456789”.
Tuż przed wykonaniem „$” najwyższe elementy stosu to:
„$” Wykonuje blok raz dla każdego elementu tablicy, który ma zostać posortowany, po naciśnięciu samego elementu.
Indeks 1 w „[4 5 6 7 8 9]” wynosi -1 (brak), więc ostatni element „1420344440” jest wypychany. Daje to 48, kod ASCII odpowiadający znakowi 0. W przypadku 2 i 3 48 jest również wypychane.
Liczby całkowite wypchnięte dla 4, 5, 6, 7, 8 i 9 to 49, 52, 50, 48, 51 i 52.
Po posortowaniu pierwszym elementem stanu będzie jeden z elementów dających 48; ostatnia będzie jedną z tych, które dają 52. Wbudowany rodzaj jest ogólnie niestabilny, ale empirycznie zweryfikowałem, że jest stabilny w tym konkretnym przypadku.
Wynik to „[1 2 3 7 4 6 8 5 9]”, co odpowiada obrotowi lewego dolnego kwadratu w prawo.
Kod zoptymalizowany pod kątem prędkości
Zauważ, że obroty 3, 0, 7, 6 i 4 zamieniają elementy w pozycjach 7 i 8, nie zmieniając pozycji pozostałych siedmiu elementów.
źródło
Python z Numpy - 158
Dane wejściowe muszą mieć następujący format:
Każda linia wyjściowa jest ruchem kodowanym w ciągach takich jak
trw
lubblc
i należy ją czytać w następujący sposób:t
: Topb
: Dolnyl
: lewor
: dobrzec
: zgodnie z ruchem wskazówek zegaraw
: przeciwnie do ruchu wskazówek zegara (widdershins)Ten program wykonuje losowe ruchy, aż do osiągnięcia docelowej konfiguracji. Przy przybliżonym założeniu, że każdy ruch ma niezależne prawdopodobieństwo 1/9! aby trafić w konfigurację docelową¹, liczba obrotów przed rozwiązaniem rozkłada się wykładniczo ze średnią (tj. średnią liczbą ruchów) 9! ≈ 3,6 · 10⁵. Jest to zgodne z krótkim eksperymentem (20 przebiegów).
¹ 9! będący całkowitą liczbą konfiguracji.
źródło
C ++ najmniej ruchów rozwiązanie - najpierw szerokość (1847 znaków)
Po dłuższej refleksji myślę, że zrobiłem to znacznie wydajniej i rozsądniej. To rozwiązanie, choć z pewnością nie wygrywa w golfa, jest jak dotąd jedynym, które spróbuje znaleźć najmniejszą liczbę obrotów, które rozwiążą planszę. Jak dotąd rozwiązuje każdą losową planszę, którą rzuciłem na nią w dziewięciu lub mniej ruchach. Działa również znacznie lepiej niż mój ostatni i, mam nadzieję, odpowiada na poniższe uwagi Dennisa.
W porównaniu z poprzednim rozwiązaniem największą zmianą było przeniesienie kluczowej historii ze stanu tablicy (BS) do nowej klasy, która przechowuje historię na określonej głębokości (DKH). Za każdym razem, gdy aplikacja wykonuje ruch, sprawdza historię na tej głębokości i wszystkich głębokościach, aby sprawdzić, czy kiedykolwiek została oceniona, jeśli tak, to nie zostanie ponownie dodana do kolejki. Wydaje się, że znacznie zmniejsza to miejsce w kolejce (poprzez usunięcie całej tej historii z samego stanu płyty), a zatem zmniejsza prawie całe głupie przycinanie, które musiałem zrobić, aby zabrakło pamięci. Dodatkowo działa znacznie szybciej, ponieważ do kolejki jest znacznie mniej kopii.
Teraz jest to proste, szerokie pierwsze wyszukiwanie w różnych stanach planszy. Ponadto, jak się okazuje, chcę zmienić zestaw kluczy (obecnie przechowywany jako zestaw liczb w bazie-9, z których każdy jest obliczany przez BS :: key jako reprezentacja tablicy w bazie-9) na zestaw bitów mając 9! bity wydają się niepotrzebne; chociaż dowiedziałem się, jak obliczyć klucz w „systemie liczb silnych”, który mógł zostać wykorzystany do obliczenia bitu w zestawie bitów w celu przetestowania / przełączenia.
Tak więc najnowszym rozwiązaniem jest:
źródło
int[]
naconst int[]
i ustawić flagę-fpermissive
.CJam - 39
Kolejny losowy solver :)
Pobiera ciąg taki jak 492357816 i wyświetla (długą) serię cyfr od 0 do 3, z których każda reprezentuje obrót bloku zgodnie z ruchem wskazówek zegara: 0 = lewy górny, 1 = prawy górny, 2 = dolny -lewa, 3 = prawy dolny róg.
Krótkie wyjaśnienie:
4mr
generuje liczbę losową od 0 do 3_1>+
przyrostów, jeśli jest większa niż 1 (więc kończymy na 0, 1, 3 lub 4 - indeksy początkowe 4 bloków)m<
obraca ciąg w lewo (np. 492357816 -> 923578164, a nie obrót bloku), aby obrócić blok w pierwszej pozycji,[Z0Y4X]\f=
wykonuje obrót bloku, który wpływa na pierwsze 5 znaków, na przykład 12345 -> 41352;X = 1, Y = 2, Z = 3, więc [Z0Y4X] to w rzeczywistości [3 0 2 4 1], a są to indeksy 0 obracanych płytek
5>
kopiuje, reszta łańcucham>
obraca (zmodyfikowany) ciąg z powrotem do prawo__$>
sprawdza, czy łańcuch jest posortowany (jest to warunek zatrzymania)źródło
Mathematica, 104 znaki
Możemy interpretować to zadanie w języku grup permutacyjnych. Cztery obroty to tylko cztery permutacje, które generują symetryczną grupę S 9 , a zadaniem jest po prostu napisanie permutacji jako iloczynu generatorów. Mathematica ma do tego wbudowaną funkcję.
Przykład:
Wejście:
Wynik:
1
: w lewym górnym rogu zgodnie z ruchem wskazówek zegara2
: w prawym górnym rogu zgodnie z ruchem wskazówek zegara3
: w prawym dolnym rogu zgodnie z ruchem wskazówek zegara4
: u dołu po lewej zgodnie z ruchem wskazówek zegara-1
: w lewym górnym rogu przeciwnie do ruchu wskazówek zegara-2
: w prawym górnym rogu przeciwnie do ruchu wskazówek zegara-3
: u dołu po prawej przeciwnie do ruchu wskazówek zegara-4
: u dołu po lewej przeciwnie do ruchu wskazówek zegaraźródło