Siatkówka , 66 63 45 43 36 bajtów
^()(\1(?<1>.\1))+(\1(.(?(4).\4)))*$
Pomimo tytułu Retina, jest to zwykły regex .NET, który akceptuje jednoargumentowe przedstawienie liczb Loeschiana.
Wejścia 999 i 1000 zajmują znacznie mniej niż sekundę.
Wypróbuj online! (Pierwszy wiersz włącza pakiet testowy oddzielony od linii, a kolejne dwa zajmują się konwersją na unary dla wygody).
Wyjaśnienie
Rozwiązanie opiera się na klasyfikacji, według której dane wejściowe można zapisać jako i*i + j*(i + j)
dodatnie i
i nieujemne j
(ponieważ nie musimy obsługiwać danych wejściowych 0
), i n*n
jest to tylko suma pierwszych n
nieparzystych liczb całkowitych. Gra w golfa była ciekawym ćwiczeniem w referencjach.
„Odwołanie do przodu” ma miejsce, gdy umieścisz odsyłacz wsteczny w grupie, do której się odnosi. Oczywiście to nie działa, gdy grupa jest używana za pierwszym razem, ponieważ nie ma jeszcze żadnych odnośników, ale jeśli umieścisz to w pętli, wówczas odniesienie wsteczne pobierze za każdym razem poprzednią iterację. To z kolei pozwala budować większe przechwytywanie z każdą iteracją. Może to być wykorzystane do stworzenia bardzo zwartych wzorów dla takich rzeczy, jak liczby trójkątne, kwadraty i liczby Fibonacciego.
Jako przykład, wykorzystując fakt, że kwadraty są tylko sumami pierwszych n
nieparzystych liczb całkowitych, możemy dopasować kwadratowe dane wejściowe w następujący sposób:
(^.|..\1)+$
Przy pierwszej iteracji ..\1
nie może działać, ponieważ \1
nie ma jeszcze wartości. Zaczniemy więc od ^.
uchwycenia pojedynczej postaci w grupie 1
. W kolejnych iteracjach ^.
nie pasuje już z powodu zakotwiczenia, ale teraz ..\1
jest poprawny. Dopasowuje dwa znaki więcej niż w poprzedniej iteracji i aktualizuje przechwytywanie. W ten sposób dopasowujemy rosnące liczby nieparzyste, uzyskując kwadrat po każdej iteracji.
Niestety nie możemy obecnie korzystać z tej techniki. Po dopasowaniu i*i
musimy również uzyskać i
, abyśmy mogli go pomnożyć j
. Prostym (ale długim) sposobem na zrobienie tego jest skorzystanie z faktu, że dopasowywanie i*i
wymaga i
iteracji, aby uchwycić i
rzeczy w grupie 1
. Możemy teraz użyć grup bilansujących, aby to wyodrębnić i
, ale tak jak powiedziałem, jest to drogie.
Zamiast tego wymyśliłem inny sposób na zapisanie tej „sumy kolejnych nieparzystych liczb całkowitych”, która daje również i
grupę przechwytującą na końcu. Oczywiście i
ta nieparzysta liczba jest po prostu 2i-1
. To daje nam sposób na zwiększenie referencji do przodu tylko o 1 na każdej iteracji. To jest ta część:
^()(\1(?<1>.\1))+
To ()
po prostu popycha puste przechwytywanie do grupy 1
(inicjowanie i
do 0
). Jest to prawie równoważne z ^.|
powyższym prostym rozwiązaniem, ale użycie |
w tym przypadku byłoby nieco trudniejsze.
Następnie mamy główną pętlę (\1(?<1>.\1))
. \1
dopasowuje poprzednie i
, (?<1>.\1)
a następnie aktualizuje grupę za 1
pomocą i+1
. Jeśli chodzi o nowe i
, właśnie dopasowaliśmy 2i-1
postacie. Właśnie tego potrzebujemy.
Kiedy skończyliśmy, dopasowaliśmy trochę kwadratu, i*i
a grupa 1
nadal trzyma i
postacie.
Druga część jest bliższa prostemu dopasowaniu do kwadratu, który pokazałem powyżej. Zignorujmy na razie odwołanie do 1
:
(.(?(4).\1))*
Jest to w zasadzie to samo co (^.|..\4)*
, z tym wyjątkiem, że nie możemy z nich skorzystać, ^
ponieważ nie jesteśmy na początku łańcucha. Zamiast tego używamy warunkowego, aby dopasować dodatkowe .\1
tylko wtedy, gdy już korzystaliśmy z grupy 4
. Ale w rzeczywistości jest dokładnie tak samo. To nam daje j*j
.
Brakuje tylko j*i
terminu. Łączymy to z j*j
faktem, że j*j
obliczenia wciąż wymagają j
iteracji. Więc dla każdej iteracji przesuwamy kursor i
o \1
. Musimy tylko upewnić się, że nie zapisujesz tego w grupie 4
, ponieważ byłoby to bałaganem przy dopasowywaniu kolejnych liczb nieparzystych. W ten sposób dochodzimy do:
(\1(.(?(4).\1)))*