Idealne tablice rejestracyjne
Zaczynam kilka lat temu, prowadząc małą grę podczas jazdy: sprawdzając, czy pobliskie tablice rejestracyjne są „idealne”. Jest to stosunkowo rzadkie, ale ekscytujące, gdy go znajdziesz.
Aby sprawdzić, czy tablica rejestracyjna jest idealna:
- Sumuj znaki, z A = 1, B = 2, ... Z = 26.
- Weź każdą kolejną część cyfr i zsumuj je; pomnóż każdą z tych sum razem.
Jeśli wartości w części 1 i 2 są równe, gratulacje! Znalazłeś idealną tablicę rejestracyjną!
Przykłady
License plate: AB3C4F
Digits -> 3 * 4
= 12
Chars -> A + B + C + F
= 1 + 2 + 3 + 6
= 12
12 == 12 -> perfect!
License plate: G34Z7T
Digits -> (3 + 4) * 7
= 49
Chars -> G + Z + T
= 7 + 26 + 20
= 53
49 != 53 -> not perfect!
License plate: 10G61
Digits -> (1 + 0) * (6 + 1)
= 7
Chars -> G
= 7
7 == 7 -> perfect!
Wyzwanie
Jako przykłady użyłem tablic rejestracyjnych o długości 5 i 6, ale ta procedura obowiązuje dla dowolnej długości tablicy rejestracyjnej. Twoim wyzwaniem jest, dla danej długości N, zwrócenie liczby idealnych tablic rejestracyjnych o tej długości. Dla celów wyzwania ważną tablicą rejestracyjną jest dowolna kombinacja cyfr 0–9 i znaków AZ. Tabliczka musi zawierać zarówno znak, jak i cyfrę, aby uznać ją za potencjalnie idealną. Dla celów kontrolnych oto wartości, które otrzymałem (chociaż nie mogę w 100% mówić o ich poprawności, hahaha)
N < 2: 0
N = 2: 18
N = 3: 355
N = 4: 8012
Notatki
Jeśli w jakiś sposób uprości to problem w twoim języku, możesz przekazać proporcję idealnych tablic rejestracyjnych dla danego N, do co najmniej 2 cyfr znaczących.
N < 2: 0
N = 2: 0.0138888...
N = 3: 0.0076088...
N = 4: 0.0047701...
LUB możesz podać wartość równoważną mod 256
N < 2: 0
N = 2: 18
N = 3: 99
N = 4: 76
Najkrótsze wygrane!
N
.Odpowiedzi:
Python 3.6, 150 bajtów
wyniki:
Wersja bez golfa z wyjaśnieniem:
Problem sprowadza się do przeszukania drzewa, w którym każdy poziom drzewa odpowiada pozycji w numerze rejestracyjnym, a każdy węzeł ma 36 dzieci (10 cyfr i 26 liter). Ta funkcja dokonuje rekurencyjnego przeszukiwania drzewa, gromadząc wartości cyfr i liter.
Zawiera golf, konwertując pętle for na sumy generatorów:
Następnie łączenie generatorów. Zakoduj litery od A do Z, od -1 do -26, a cyfry od 0 do 9. Tak więc suma staje się:
gdzie args to:
Reszta gry w golfa polega na zamianie funkcji na lambda, skracaniu nazw zmiennych i upraszczaniu wyrażeń.
źródło
n*n*log(n)
czy coś podobnego?Dyalog APL,
5756 bajtów(zakłada
⎕io←0
)a
macierz wszystkich ważnych tablic rejestracyjnych (oprócz00...0
) kodowanych: 0-9 dla cyfr, 10-35 dla literb
maska bitowa dla miejsca występowania cyfrc
maska bitowa dla ostatniej cyfry w każdej grupie kolejnych cyfrźródło
Python 2,
359295 bajtówRaczej długi; to jest trywialne rozwiązanie. Jestem pewien, że jest to poprawne, choć nie pasuje do przypadków testowych w wyzwaniu. Rozwiązania, które otrzymuję, pasują do odpowiedzi Dady.
-64 bajty dzięki sugestiom @numbermaniac
źródło
for
; pomiędzymap(ord,x)
iif
; aw ostatniej linii pomiędzy.join(x)
ifor
. Myślę, że możesz zaoszczędzić jeszcze więcej, jeśli przedefiniujesz funkcje na lambdas.Python 2 ,
291287276273 bajtówWypróbuj online!
Wyniki:
źródło
Perl 5 , 117 bajtów
116 bajtów kodu +
-p
flaga.Wypróbuj online!
Wydaje się to dość nieoptymalne, ale obecnie brakuje mi pomysłów.
Sam kod jest bardzo nieefektywny, ponieważ oblicza każdą permutację
a..z,0..9
długościn
(zajmuje to około 1 sekundy dlan=3
, ~ 15 sekund dlan=4
i ~ 7 minut dlan=5
).Algorytm jest dość prosty: dla każdej możliwej płytki wielkości
n
(wygenerowanej za pomocąglob"{@F}"x$_
-glob
operator jest dość magiczny)$r*=eval s/./+$&/gr for/\d+/g;
oblicza iloczyn każdego kawałka cyfr i$r+=64-ord for/\pl/g
odejmuje od niego wagę liter. Następnie zwiększamy licznik,$\
jeśli$r
is0
(!$r
) i jeśli tablica zawiera cyfry i litery (/\pl/*/\d/
).$\
jest niejawnie wydrukowany na końcu dzięki-p
flagi.Należy pamiętać, że numery mogę uzyskać to
n=2 -> 18
,n=3 -> 355
,n=4 -> 8012
,n=5 -> 218153
. Jestem pewien, że to są właściwe, ale mogę się mylić, w takim przypadku daj mi znać, a ja usunę tę odpowiedź.źródło
APL (Dyalog) , 71 bajtów
Pełna treść programu. Monity o N. N≥4 wymagają ogromnej ilości pamięci i obliczeń.
Wypróbuj online!
źródło
Scala, 265 bajtów
Objaśnienia:
Uwagi:
-64
i-48
są stosowane do przekształceniaChar
(odpowiednio litery i cyfry) do jegoInt
wartości ('A' - 64 = 1
,'B' - 64 = 2
, ...,'9' - 48 = 9
)l.split("[A-Z]").filter(""<)
służy do eliminacji""
wartości, jeślil
zaczyna się na literę (przykład"A1".split("[A-Z]") => Array("", 1)
:). Może być lepsze i krótsze rozwiązaniePrzypadki testowe :
Wyniki:
Ta funkcja działa dość wolno,
n > 4
ponieważ wszystkie kombinacje muszą zostać wygenerowane.źródło
Java,
382365 bajtówGrał w golfa
Szczegółowy
źródło
n
dane wejściowe.int h(String s){int m=0;for(int c:s.toCharArray())m+=c-48;return m;}int g(String t){int d=1,c=0;for(String s:t.split("[^0-9]"))d*=h(s);for(String s:t.split("[^A-Z]"))c+=s.charAt(0)-65;return d==c?1:0;}int f(String t,int n){int m=0;if(t.length()==n)return g(t);for(int d=48;d<58;)m+=f(t+d++,n);for(int c=65;c<91;)m+=f(t+c++,n);return m;}int s(int n){return f("",n);}
( 365 bajtów ) Możesz porównać swoją obecną wersję z tą, aby zobaczyć zmiany, które wprowadziłem (zbyt wiele, by zmieścić się w pozostałej części tego komentarza). :)LUKA , 416 bajtów
Nie wygra rozmiaru kodu, i to daleko od stałego czasu, ale używa matematyki, aby znacznie przyspieszyć!
Aby wycisnąć niepotrzebne białe znaki i uzyskać jedną linię z 416 bajtami, przeciągnij przez to:
Mój stary laptop „zaprojektowany dla systemu Windows XP” może obliczyć
f(10)
w mniej niż minutę i pójść znacznie dalej w ciągu godziny:Jak to działa
Załóżmy, że najpierw chcemy znać tylko liczbę idealnych tablic rejestracyjnych pasujących do wzoru
LDDLLDL
, gdzieL
oznacza literę, aD
cyfrę. Załóżmy, że mamy taką listęl
liczb, któral[i]
podaje liczbę sposobów, w jakie litery mogą podawać wartośći
, oraz podobną listęd
wartości, które otrzymujemy z cyfr. Zatem liczba idealnych tablic rejestracyjnych o wspólnej wartościi
jest po prostul[i]*d[i]
i otrzymujemy liczbę wszystkich idealnych tablic rejestracyjnych według naszego wzoru, sumując to wszystkoi
. Oznaczmy operację uzyskania tej sumyl@d
.Teraz nawet jeśli najlepszym sposobem na uzyskanie tych list było wypróbowanie wszystkich kombinacji i policzenie, możemy to zrobić niezależnie dla liter i cyfr, patrząc na
26^4+10^3
przypadki zamiast26^4*10^3
przypadków, gdy po prostu przeglądamy wszystkie tablice pasujące do wzoru. Ale możemy zrobić znacznie lepiej: tutajl
jest tylko lista współczynników,(x+x^2+...+x^26)^k
gdziek
jest liczba liter4
.Podobnie otrzymujemy liczbę sposobów uzyskania sumy cyfr w szeregu
k
cyfr jako współczynników(1+x+...+x^9)^k
. Jeśli jest więcej niż jeden ciąg cyfr, musimy połączyć odpowiednie listy z operacją,d1#d2
która na pozycjii
ma jako wartość sumę wszystkichd1[i1]*d2[i2]
gdzie . Wraz z faktem, że jest dwuliniowy, daje to miły (ale niezbyt wydajny) sposób na jego obliczenie.i1*i2=i
. Jest to splot Dirichleta, który jest produktem, jeśli interpretujemy listy jako współczynniki serii Dirchleta. Ale używaliśmy ich już jako wielomianów (skończone szeregi mocy) i nie ma dobrego sposobu na interpretację ich działania. Myślę, że to niedopasowanie jest częścią tego, co utrudnia znalezienie prostej formuły. W każdym razie użyjmy go na wielomianach i zastosujmy tę samą notację#
. Łatwo jest obliczyć, gdy jeden operand jest monomialny: mamyp(x) # x^k = p(x^k)
Zauważ, że
k
litery dają co najwyżej wartość26k
, podczas gdyk
pojedyncze cyfry mogą dawać wartość9^k
. Tak więc często otrzymamy niepotrzebne wysokie moce wd
wielomianu. Aby się ich pozbyć, możemy obliczyć modulox^(maxlettervalue+1)
. Daje to duże przyspieszenie i chociaż nie zauważyłem od razu, nawet pomaga w grze w golfa, ponieważ teraz wiemy, że stopieńd
nie jest większy niż tenl
, co upraszcza górną granicę w finaleSum
. Jeszcze lepsze przyspieszenie uzyskujemy, wykonującmod
obliczenia w pierwszym argumencieValue
(patrz komentarze), a wykonanie całego#
obliczenia na niższym poziomie daje niesamowite przyspieszenie. Ale wciąż staramy się być uzasadnioną odpowiedzią na problem golfowy.Więc mamy nasze
l
id
i można z nich korzystać, aby obliczyć liczbę doskonałych tablic rejestracyjnych z wzoremLDDLLDL
. To ta sama liczba, co dla wzoruLDLLDDL
. Ogólnie rzecz biorąc, możemy zmienić kolejność ciągów cyfr o różnej długości, jak nam się podoba,NrArrangements
daje to szereg możliwości. I chociaż między ciągami cyfr musi znajdować się jedna litera, pozostałe litery nie są ustalone.Binomial
Liczy te możliwości.Teraz pozostaje przejść przez wszystkie możliwe sposoby uzyskania długości cyfr przebiegów.
r
przebiega przez wszystkie liczby przebiegów,c
przez całkowitą liczbę cyfr ip
przez wszystkie partycjec
zr
sumami.Całkowita liczba partycji, które przeglądamy, jest o dwa razy mniejsza niż liczba partycji
n+1
, a funkcje partycji rosną podobnieexp(sqrt(n))
. Tak więc, chociaż wciąż istnieją proste sposoby na poprawę czasu działania poprzez ponowne wykorzystanie wyników (przeglądanie partycji w innej kolejności), dla fundamentalnej poprawy musimy unikać oddzielnego patrzenia na każdą partycję.Obliczam to szybko
Zauważ, że
(p+q)@r = p@r + q@r
. Samo to pomaga po prostu uniknąć mnożenia. Ale razem z(p+q)#r = p#r + q#r
tym oznacza, że możemy łączyć poprzez proste dodawanie wielomianów odpowiadających różnym partycjom. Nie możemy po prostu dodać ich wszystkich, ponieważ wciąż musimy wiedzieć, z czyml
musimy@
połączyć, jaki czynnik musimy zastosować i które#
rozszerzenia są nadal możliwe.Połączmy wszystkie wielomiany odpowiadające partycjom z tą samą sumą i długością, i już uwzględniliśmy wiele sposobów dystrybucji długości ciągów cyfr. W odróżnieniu od tego, co spekulowałem w komentarzach, nie muszę się martwić o najmniejszą używaną wartość lub częstotliwość jej używania, jeśli upewnię się, że nie będę rozszerzać o tę wartość.
Oto mój kod C ++:
Wykorzystuje bibliotekę GNU MP. W Debianie zainstaluj
libgmp-dev
. Kompiluj zg++ -std=c++11 -O3 -o pl pl.cpp -lgmp -lgmpxx
. Program bierze swój argument ze standardowego wejścia. Do pomiaru czasu użyjecho 100 | time ./pl
.Na końcu
a[sum][length][i]
podaje liczbę sposobów, w jakiesum
cyfry wlength
seriach mogą podawać liczbęi
. Podczas obliczeń, na początkum
pętli, podaje liczbę sposobów, które można zrobić z liczbami większymi niżm
. Wszystko zaczyna się oda[0][0][1]=1
. Zauważ, że jest to nadzbiór liczb potrzebnych do obliczenia funkcji dla mniejszych wartości. Więc prawie w tym samym czasie moglibyśmy obliczyć wszystkie wartości don
.Nie ma rekurencji, więc mamy stałą liczbę zagnieżdżonych pętli. (Najgłębszy poziom zagnieżdżenia to 6.) Każda pętla przechodzi przez szereg wartości, które
n
w najgorszym przypadku są liniowe . Potrzebujemy tylko czasu wielomianowego. Jeśli przyjrzymy się bliżej zagnieżdżeniui
ij
pętliextend
, znajdziemy górną granicęj
formyN/i
. To powinno dać jedynie współczynnik logarytmiczny dlaj
pętli. Najbardziej wewnętrzna pętla wf
(zsumn
etc) jest podobna. Należy również pamiętać, że obliczamy z szybko rosnącymi liczbami.Pamiętaj również, że przechowujemy
O(n^3)
te liczby.Eksperymentalnie otrzymuję te wyniki na rozsądnym sprzęcie (i5-4590S):
f(50)
potrzebuje jednej sekundy i 23 MB,f(100)
potrzebuje 21 sekund i 166 MB,f(200)
potrzebuje 10 minut i 1,5 GB orazf(300)
potrzebuje godziny i 5,6 GB. To sugeruje złożoność czasu lepszą niżO(n^5)
.źródło
n=5
nie ma przypadek z ciągiem dwóch cyfr i dwóch pojedynczych cyfr, ponieważ wtedy nie mamy wystarczającej liczby liter do oddzielenia liczb. To właśnie robią trzy zewnętrznefor
pętle: przeglądaj wszystkie przydatne partycje liczb<n
. (I właśnie zdałem sobie sprawę, że dopuszczam równieżn
cyfry. Na szczęście kolejna optymalizacja liczy to jako 0).<n/2
, wszystkie partycje są użyteczne. A pozostałe obliczenia wciąż zajmują nieregularny czas. Aby zobaczyć, co się dzieje, możesz dodaćPrint(p,"\n");
na początku treścifor p...
pętli. - Wpadłem na pomysł, aby użyć o jedną pętlę mniej, ale pomoże to tylko w rozmiarze kodu.mod
(co już bardzo pomogło) doValue
, zmieniając naValue(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1))
. Samo to pozwala obliczyćf(15)
w 80 sekund.Pyth, 55 bajtów
źródło