Zadanie
Biorąc pod uwagę wejście N, wygeneruj i wyślij siatkę NxN, w której każdy wiersz, kolumna i dwie przekątne zawierają liczby od 1 do N
(lub od 0 do N
-1, jeśli jest to łatwiejsze).
Wkład
Dane wejściowe to dodatnia liczba całkowita N
. Reprezentuje liczbę kolumn i wierszy w siatce. W przypadku tego problemu można założyć N
, że będzie to rozsądny rozmiar 4 ≤ N ≤ 8
lub ( 1 ≤ N ≤ 8
jeśli skorzystasz z bonusu poniżej).
Wydajność
Wyjściem będzie siatka N
× N
. W siatce każdy wiersz zawiera tylko liczby od 1 do N
, każda kolumna zawiera tylko liczby od 1 do N
, a dwie przekątne długości N
(jedna od (0,0)
do (N-1,N-1)
i jedna od (0,N-1)
do (N-1, 0)
) zawierają tylko liczby od 1 do N
. Możesz użyć cyfr od 0 do N−1
. Dla każdego N
istnieje wiele możliwych rozwiązań, wystarczy wydrukować tylko pierwsze znalezione. Nie musisz drukować spacji między liczbami.
Ograniczenia
Twój kod powinien być w stanie powtarzalnie generować wyniki dla N >= 7
. Oznacza to, że jeśli jesteś w stanie uruchomić i za N = 7
każdym razem uzyskać rozwiązanie z kodu, jesteś dobry. Jeśli chodzi o limit bezwzględny, Twój kod powinien być w stanie rozwiązać N = 7
w ciągu mniej niż 10 minut za każdym razem, gdy go uruchomisz (tj. Jeśli zależysz od liczb losowych, w najgorszym przypadku kod powinien nadal kończyć się w mniej niż 10 minut N = 7
) .
Przykłady
Wkład:
4
Jedno możliwe wyjście:
1 2 3 4 3 4 1 2 4 3 2 1 2 1 4 3
Wkład:
5
Jedno możliwe wyjście:
1 2 3 4 5 5 3 1 2 4 2 5 4 3 1 4 1 2 5 3 3 4 5 1 2
Wkład:
8
Jedno możliwe wyjście:
1 2 3 4 5 6 7 8 2 3 1 5 4 8 6 7 4 1 2 3 7 5 8 6 6 4 7 8 1 2 3 5 7 5 8 2 6 3 4 1 5 8 4 6 2 7 1 3 8 7 6 1 3 4 5 2 3 6 5 7 8 1 2 4
Punktacja
To jest golf golfowy , więc wygrywa najkrótszy kod w bajtach, z jednym wyjątkiem. W przypadku danych wejściowych N = 2, 3
nie ma poprawnych rozwiązań. Jeśli Twój kod może to obsłużyć (uruchomić do końca bez wypisywania czegokolwiek w tych przypadkach lub wypisać pusty ciąg znaków), a mimo to obsługuje N = 1
(dane wyjściowe 1
dla niego), zmniejsz o 20% liczbę bajtów.
źródło
N
. Ten kod JavaScript działaN = 1, 5 or 7
jednak, jeśli pomaga komukolwiek:for(o="",y=N;y--;o+="\n")for(x=N;x--;)o+=(((N-y)*2+x)%N)+1
N = 1
sprawie: odpowiedzi, które mają na celu uzyskanie premii, powinny zostać zwrócone1
, a nie pusty ciąg znaków.Odpowiedzi:
Python 3,
275260 bajtów * 0,8 =220208 bajtówPodejście rekurencyjne / wycofywanie.
R
jest funkcją rekurencyjną,l
jest kolumną,w
jest linią,K
jest następnym wpisem.Zdecydowałem się umieścić go w tablicy 1d i ładnie wydrukować na końcu, aby uprościć indeksy.
Wersja bez golfa:
źródło
Funciton , niekonkurencyjny
AKTUALIZACJA! Ogromna poprawa wydajności! n = 7 kończy się teraz w mniej niż 10 minut! Zobacz wyjaśnienie na dole!
To była dobra zabawa pisać. To jest solute brute-force dla tego problemu napisanego w Funciton. Niektóre faktoidy:
3 2 1 0
a nie górny0 1 2 3
.0
(jedyne rozwiązanie) dla n = 1.Bez ceregieli:
Objaśnienie pierwszej wersji
Pierwsza wersja zajęła około godziny n = 7. Poniżej wyjaśniono głównie, jak działała ta wolna wersja. Na dole wyjaśnię, jakie zmiany wprowadziłem, aby uzyskać mniej niż 10 minut.
Wycieczka na kawałki
Ten program potrzebuje bitów. Potrzebuje wielu bitów i potrzebuje ich we wszystkich właściwych miejscach. Doświadczeni programiści Funciton już wiedzą, że jeśli potrzebujesz n bitów, możesz użyć wzoru
które w Funcitonie można wyrazić jako
Podczas optymalizacji wydajności przyszło mi do głowy, że mogę obliczyć tę samą wartość znacznie szybciej, korzystając z tego wzoru:
Mam nadzieję, że wybaczysz mi, że nie zaktualizowałem odpowiednio całej grafiki równania w tym poście.
Powiedzmy, że nie chcesz ciągłego bloku bitów; tak naprawdę chcesz n bitów w regularnych odstępach co k -ty bit, tak jak poniżej:
Wzór na to jest dość prosty, gdy się zorientujesz:
W kodzie funkcja
Ӝ
przyjmuje wartości n i k i oblicza tę formułę.Śledzenie używanych numerów
Na końcowej siatce jest n ² liczb, a każda liczba może być dowolną z n możliwych wartości. Aby śledzić, które liczby są dozwolone w każdej komórce, zachowujemy liczbę składającą się z n ³ bitów, w których ustawiony jest bit wskazujący, że określona wartość jest pobierana. Początkowo liczba ta wynosi oczywiście 0.
Algorytm rozpoczyna się w prawym dolnym rogu. Po „odgadnięciu” pierwszą liczbą jest 0, musimy śledzić fakt, że 0 nie jest już dozwolone w żadnej komórce wzdłuż tego samego wiersza, kolumny i przekątnej:
W tym celu obliczamy następujące cztery wartości:
Bieżący wiersz: Potrzebujemy n bitów co n- ty bit (jeden na komórkę), a następnie przesuwamy go do bieżącego wiersza r , pamiętając, że każdy wiersz zawiera n ² bitów:
Bieżąca kolumna: Potrzebujemy n bitów co n ²-ty bit (jeden na wiersz), a następnie przesuwamy ją do bieżącej kolumny c , pamiętając, że każda kolumna zawiera n bitów:
Przekątna do przodu: Potrzebujemy n bitów co ... (czy zwracałeś uwagę? Szybko, wymyśl to!) ... n ( n +1) -ty bit (dobra robota!), Ale tylko jeśli faktycznie jesteśmy na przekątna do przodu:
Przekątna do tyłu: tutaj dwie rzeczy. Po pierwsze, skąd wiemy, czy jesteśmy na przekątnej do tyłu? Matematycznie warunkiem jest c = ( n - 1) - r , co jest takie samo jak c = n + (- r - 1). Hej, czy to ci coś przypomina? Zgadza się, to uzupełnienie dwóch, więc zamiast dekrementacji możemy użyć bitowej negacji (bardzo wydajnej w Funcitonie). Po drugie, powyższy wzór zakłada, że chcemy ustawić najmniej znaczący bit, ale tak nie jest w przypadku przekątnej wstecznej, więc musimy go przesunąć w górę o ... wiesz? ... Zgadza się, n ( n - 1).
Jest to również jedyny przypadek, w którym potencjalnie dzielimy przez 0, jeśli n = 1. Jednak Funciton to nie obchodzi. 0 ÷ 0 to tylko 0, nie wiesz?
W kodzie funkcja
Җ
(dolna) przyjmuje n, a indeks (na podstawie którego oblicza r i c przez dzielenie i resztę), oblicza te cztery wartości ior
s je razem.Algorytm brutalnej siły
Algorytm siły brutalnej jest implementowany przez
Ӂ
(funkcja u góry). Zajmuje n (rozmiar siatki), indeks (gdzie na siatce obecnie umieszczamy liczbę) i pobierane (liczba z n ³ bitami mówi nam, które liczby możemy nadal umieścić w każdej komórce).Ta funkcja zwraca sekwencję ciągów. Każdy ciąg jest pełnym rozwiązaniem dla siatki. To kompletny solver; zwróci wszystkie rozwiązania, jeśli na to pozwolisz, ale zwróci je jako sekwencję leniwą.
Jeśli indeks osiągnął wartość 0, pomyślnie wypełniliśmy całą siatkę, więc zwracamy sekwencję zawierającą pusty ciąg (pojedyncze rozwiązanie, które nie obejmuje żadnej komórki). Jest pusty ciąg
0
i używamy funkcji biblioteki,⌑
aby przekształcić go w sekwencję jednoelementową.Sprawdzanie opisane w poniższej poprawie wydajności ma miejsce tutaj.
Jeśli indeks nie osiągnął jeszcze 0, zmniejszamy go o 1, aby uzyskać indeks, pod którym teraz musimy umieścić liczbę (nazwij to IX ).
Używamy
♫
do generowania leniwej sekwencji zawierającej wartości od 0 do n - 1.Następnie używamy
ɓ
(monadic bind) z lambda, która wykonuje następujące czynności w kolejności:0
(pusta sekwencja).Җ
do obliczania bitów odpowiadających bieżącemu rzędowi, kolumnie i przekątnej. Przesuń go o i, a następnieor
na zabrane .Ӂ
aby pobrać wszystkie rozwiązania dla pozostałych komórek, przekazując mu nowe zajęte i zmniejszone ix . Zwraca sekwencję niekompletnych ciągów; każdy ciąg ma znaki ix (siatka wypełniona do indeksu ix ).ɱ
(map), aby przejrzeć znalezione w ten sposób rozwiązania, i użyj,‼
aby połączyć i do końca każdego z nich. Dodaj nowy wiersz, jeśli indeks jest wielokrotnością n , w przeciwnym razie spacją.Generowanie wyniku
Główne wywołania programu
Ӂ
(brutalny forcer) z n , indeks = n ² (pamiętaj, że wypełniamy siatkę do tyłu) i przyjmowane = 0 (początkowo nic nie jest pobierane). Jeśli wynikiem tego jest pusta sekwencja (nie znaleziono rozwiązania), wypisz pusty ciąg. W przeciwnym razie wypisz pierwszy ciąg w sekwencji. Zauważ, że oznacza to, że będzie oceniał tylko pierwszy element sekwencji, dlatego solver nie kontynuuje działania, dopóki nie znajdzie wszystkich rozwiązań.Poprawa wydajności
(Dla tych, którzy już przeczytali starą wersję objaśnienia: program nie generuje już sekwencji sekwencji, którą należy osobno przekształcić w ciąg wyjściowy; po prostu generuje sekwencję ciągów bezpośrednio. Odpowiednio zredagowałem wyjaśnienie Ale to nie była główna poprawa. Oto nadchodzi.)
Na moim komputerze skompilowane exe pierwszej wersji zajęło prawie dokładnie 1 godzinę na rozwiązanie n = 7. Nie było to w podanym limicie czasu 10 minut, więc nie odpoczywałem. (Właściwie powodem, dla którego nie odpoczywałem, było to, że wpadłem na pomysł, jak to znacznie przyspieszyć.)
Algorytm opisany powyżej zatrzymuje swoje wyszukiwanie i cofa się za każdym razem, że napotyka na komórkę, w którym wszystkie bity w podjętej liczby są ustawione, wskazując, że nic nie można umieścić w tej komórce.
Jednak algorytm będzie nadal bezskutecznie wypełniał siatkę do komórki, w której ustawiono wszystkie te bity. Byłoby znacznie szybciej, gdyby mógł zatrzymać się, gdy jakakolwiek komórka, która ma być wypełniona, ma już ustawione wszystkie bity, co już wskazuje, że nigdy nie możemy rozwiązać reszty siatki bez względu na to, jakie liczby wstawimy to. Ale jak sprawnie sprawdzić, czy jakakolwiek komórka ma ustawione n bitów bez przechodzenia przez wszystkie?
Sztuczka zaczyna się od dodania jednego bitu na komórkę do pobranej liczby. Zamiast tego, co pokazano powyżej, wygląda to tak:
Zamiast n ³, w tej liczbie jest teraz n ² ( n + 1) bitów. Funkcja wypełniająca bieżący wiersz / kolumnę / przekątną została odpowiednio zmieniona (w rzeczywistości całkowicie przepisana, jeśli mam być szczera). Ta funkcja będzie jednak nadal wypełniać tylko n bitów na komórkę, więc dodatkowy bit, który właśnie dodaliśmy, zawsze będzie
0
.Powiedzmy, że jesteśmy w połowie obliczeń, właśnie umieściliśmy
1
w środkowej komórce, a pobrana liczba wygląda mniej więcej tak:Jak widać, lewa górna komórka (indeks 0) i środkowa lewa komórka (indeks 10) są teraz niemożliwe. Jak najskuteczniej to ustalić?
Rozważ liczbę, w której ustawiony jest zerowy bit każdej komórki, ale tylko do bieżącego indeksu. Taka liczba jest łatwa do obliczenia za pomocą znanej formuły:
Co uzyskalibyśmy, gdyby dodać te dwie liczby razem?
Wynik to:
Jak widać, dodatek przepełnia dodatkowy bit, który dodaliśmy, ale tylko wtedy, gdy wszystkie bity dla tej komórki są ustawione! Dlatego wszystko, co pozostało do zrobienia, to maskowanie tych bitów (ta sama formuła jak powyżej, ale << n ) i sprawdzenie, czy wynikiem jest 0:
Jeśli nie jest zero, siatka jest niemożliwa i możemy się zatrzymać.
źródło
Haskell, 790 * 0,80 = 632 bajty
Zauważyłem, że ten problem jest bardzo podobny do sudoku. Pamiętam stary solver sudoku, który napisałem w Haskell na podstawie tego drugiego w Pythonie. To jest mój pierwszy post lub próba gry w golfa.
Spełnia to bonus, ponieważ wraca
Nothing
don=2,3
iJust <result>
pon>=4
, gdzie<result>
jest tablica 2D wartości całkowitych.Zobacz tutaj dla tłumacza online. Ten kod jest w rzeczywistości dłuższy niż kod pocztowy, ponieważ tłumacz online ma bardziej rygorystyczne wymagania co do tego, co tworzy kompletny program (zasady mówią, że przesłanie może być funkcją). To przesłanie przyjmuje dane wejściowe jako argument funkcji.
źródło
c=[1..r]
, abyś mógł z niego korzystać w obrębieo
iw
. b)minimumBy(\(a,_)(b,_)->compare a b)[...]
jesthead$sortOn fst[...]
. c)v
wv=g0!s
jest używany tylko raz, więc nie definiują go w ogóle:l=length$g0!s
. d) nadal masz jakieś dwuliterowe nazwy parametrów. e) zastępujeTrue
się1<2
iFalse
z2<1
. f)and[case g0!s of{[_]->True;_->False}|s<-u]
jestall((==1).length.(g0!))u
.(&)m k=
można zdefiniować Infix:m&k=
. h)not(d
elem(g0!p))
jestnotElem d$g0!p
. i)concat(...)
jestid=<<(...)
. j) użyj operatora infix dlah
npas%bs=
.``like`this``
!Pyth, 41 bajtów
Brute force ftw!
Ponieważ to w zasadzie próbuje losowych przetasowań, dopóki nie zadziała (cóż, ciągle próbuje
n * [shuffle(range(n))]
), zajmuje to naprawdę bardzo długo. Oto kilka testów, które pokazują, jak długo to trwa:To tylko 4x4 i działa w niecałe pół sekundy. Właściwie oszukuję, ponieważ jest to najlepszy z kilku prób - większość z nich zajmuje sekundę lub dwie.
Muszę jeszcze ustalić czas na 5x5 (raz skończył się, ale to było w REPL i nie mierzyłem go).
Pamiętaj, że reguła dotycząca limitu czasu została zredagowana w pytaniu dopiero po opublikowaniu tej odpowiedzi.
źródło
SWI-Prolog, 326 * 0,80 = 260,8 bajtów
Edycja: zapisano 16 bajtów dzięki @Mat
Stosowanie
Zadzwoń
a(5).
po tłumaczaN=5
. Zwraca wartośćfalse
dlaN=2
lubN=3
.Ponieważ wykorzystuje bibliotekę CLPFD, nie jest to czysta brutalność. Ten program może znaleźć rozwiązanie na
N=20
około 15 sekund na moim komputerze.Niegolfowane + objaśnienia:
Zasadniczo działa to jak solver Sudoku, z tym wyjątkiem, że ograniczenia bloków są zastępowane ograniczeniami przekątnymi.
źródło
maplist(maplist(all_distinct), [R,C,D,E])
[R,C,[D,E]]
, ponieważE
iD
są to proste listy.N=20
CJam, 87 bajtów - bonus 20% = 69,6 bajtów
Twarde kody odpowiedzi. Zawiera niektóre niedrukowalne. Działa
N = 1
przezN = 8
.Zasadniczo każda linia tego tajemniczego ciągu zawiera indeksy na liście permutacji
range(N)
, zakodowane jako znaki Unicode.=:i0+\,m!f=
indeksuje do listy permutacji, dodając najpierw 0 na końcu listy indeksów, reprezentujących dolny wiersz0 1 2 ... N-1
. PonieważN < 4
wynikowa tablica 2D jest nonsensowna.`1LL]
tworzy listę[N, pretty output, 1, "", ""]
. Następnie(4e<=
wyskakuje pierwszy element z tej listyN
i pobieramin(N, 4) % 4
element th z reszty. BoN >= 4
to jest wynik, w przeciwnym razie jest to wynik specjalnych przypadków dla pierwszych trzech przypadków.Wypróbuj tutaj .
źródło
C ++,
672 * 0,80645 * 0,80 = 516 bajtówWypróbuj online tutaj
Ponieważ już opublikowano kilka odpowiedzi, pomyślałem, że opublikuję wersję kodu w golfa, której użyłem do wygenerowania danych wyjściowych dla przykładów. Po raz pierwszy odpowiadam na golfa kodowego , więc wszelkie opinie są mile widziane. :)
Niegolfowane + objaśnienia:
Zasadniczo kod wymusza brutalne rozwiązanie. Zaczyna się w pierwszym rzędzie od 0. Zaczyna się w pierwszym miejscu, jeśli to miejsce przejdzie wszystkie kontrole, przechodzi do następnej liczby. Jeśli wypełnia wiersz, przechodzi do następnego rzędu. Jeśli zrobione są wszystkie wiersze, oznacza to, że znaleziono rozwiązanie. Jeśli miejsce nie przejdzie wszystkich testów, przechodzi do następnego miejsca. Jeśli zrobi to wiersz, to cofa się, ponieważ liczba w jednym z poprzednich wierszy uniemożliwia rozwiązanie.
źródło
if (x[r][p]) return f(c+1,r);
. Pracuję nad jego skróceniem.Clojure,
(215 + 258) * 0,8 = 378,4(174 + 255) * 0,8 = 343,2Podzielony na dwie części: liczenie błędów
S
i anonimową funkcję, która dokonuje faktycznej optymalizacji poprzez wyszukiwanie Tabu .Aktualizacja: krótsza
S
(liczy odrębne wartości w grupach), mniej optymalny stan początkowy (bez losowania).Jednordzeniowe testy porównawcze (w milisekundach) dla 4, 5, 6 i 7 uruchomionych 3 razy:
Oryginalny:
Chciałbym, aby
S
był krótszy, ale ponieważ liczy tylko wystąpienia więcej niż jednej partycji /, kryterium zatrzymania jest proste(= s 0)
.Wiele cykli procesora jest marnowanych na nieprzydatne swapy, na przykład nie poprawia wyniku, jeśli zamieniasz
2
się nimi2
, i nie musisz zamieniać liczb między wierszami, ponieważ wszystkie mają na początku różne wartości.Testy porównawcze z Intel 6700K (w milisekundach):
Wielowątkowy z
pmap
:źródło