Matthias Goergens ma 25 604 znaków (w porównaniu z pierwotną liczbą 63 993 znaków), aby dopasować liczby podzielne przez 7, ale obejmuje to wiele puchu: nadmiarowe nawiasy, dystrybucja ( xx|xy|yx|yy
zamiast [xy]{2}
) i inne problemy, chociaż jestem pewien, że nowy start byłby pomocny w oszczędzaniu miejsca. Jak mały można to zrobić?
Dowolna rozsądna różnorodność wyrażeń regularnych jest dozwolona, ale nie ma kodu wykonywalnego w wyrażeniu regularnym.
Wyrażenie regularne powinno pasować do wszystkich ciągów zawierających dziesiętną reprezentację liczby podzielnej przez 7 i żadnych innych. Dodatkowy kredyt za wyrażenie regularne, które nie pozwala na początkowe zera.
code-golf
math
regular-expression
Charles
źródło
źródło
Odpowiedzi:
10791 znaków, dozwolone zera wiodące
10795 znaków, zera wiodące zabronione
0|((foo)0*)+
, gdzie znajduje się powyższe wyrażenie regularne(0|foo)+
.Wyjaśnienie
Liczby podzielne przez 7 są dopasowywane przez oczywisty automat skończony z 7 stanami Q = {0,…, 6}, stan początkowy i końcowy 0 oraz przejścia d: i ↦ (10i + d) mod 7. Przekształciłem ten automat skończony na wyrażenie regularne, wykorzystujące rekurencję na zbiorze dozwolonych stanów pośrednich:
Biorąc pod uwagę i, j ∈ Q i S ⊆ Q, niech f (i, S, j) będzie wyrażeniem regularnym, które pasuje do wszystkich ścieżek automatów od i do j przy użyciu tylko stanów pośrednich w S.
f (i, ∅, j) = (j - 10i) mod 7,
f (i, S ∪ {k}, j) = f (i, S, j) ∣ f (i, S, k) f (k, S, k) * f (k, S, j).
Użyłem programowania dynamicznego, aby wybrać k, aby zminimalizować długość wynikowego wyrażenia.
źródło
0|((foo)0*)+
13 7551269912 731 znakówTa regex nie odrzuca wiodącego zera.
Jest to testowane z The Regex Coach .
Jak się tam dostaniemy
Powyższy Regeks powstały najpierw przez skonstruowanie DFA, który zaakceptowałby żądane dane wejściowe (dziesiętne podzielne przez 7), a następnie przekonwertował na wyrażenie regularne i naprawił notację
Aby to zrozumieć, najpierw należy utworzyć DFA, który akceptuje następujący język:
Oznacza to, że „dopasuje” liczby binarne, które można podzielić przez 7.
DFA wygląda następująco:
Jak to działa
Zachowujesz bieżącą wartość,
A
która reprezentuje wartość bitów odczytanych przez DFA. Kiedy czytasz0
a,A = 2*A
a kiedy czytasz1
A = 2*A + 1
. Na każdym kroku, który obliczyszA mod 7
, przechodzisz do stanu reprezentującego odpowiedź.Więc uruchomienie testowe:
Czytamy, w
10101
którym jest reprezentacja binarna dla 21 w systemie dziesiętnym.q0
Obecnie zaczynamy od stanuA=0
1
, ze „zasady” powyżejA = 2*A + 1
takA = 1
.A mod 7 = 1
więc przechodzimy do stanuq1
0
,A = 2*A = 2
,A mod 7 = 2
więc możemy przejść doq2
1
,A = 2*A + 1 = 5
,A mod 7 = 5
, przenieść doq5
0
,A = 2*A = 10
,A mod 7 = 3
, przenieść doq3
1
,A = 2*A + 1 = 21
,A mod 7 = 0
, przenieść doq0
10101
jest podzielna przez 7!Przekształcanie DFA w wyrażenie regularne jest trudnym zadaniem, więc poprosiłem JFLAP, aby zrobił to za mnie:
Dla liczb dziesiętnych
Proces jest prawie taki sam:
Zbudowałem DFA, który akceptuje język:
Oto DFA:
Logika jest podobna, ta sama liczba stanów, tylko wiele więcej przejść do obsługi wszystkich dodatkowych cyfr, które przynoszą liczby dziesiętne.
Teraz zasada zmienić
A
na każdym kroku jest: kiedy czytasz cyfrę dziesiętnąn
:A = 10*A + n
. Potem znowu tylkomod
A
o 7 i przejdź do następnego stanu.Rewizje
Wersja 5
Powyższe wyrażenie regularne odrzuca teraz liczby wiodące zera - oczywiście oprócz samego zera.
To sprawia, że DFA jest nieco inny, w zasadzie rozgałęziasz się od początkowego węzła, gdy czytasz pierwsze zero. Odczyt kolejnego zera wprowadza cię w nieskończoną pętlę w stanie rozgałęzionym. I nie stały schemat aby pokazać to.
Wersja 7
Zrobiłem trochę „metaregex” i skróciłem regex, zastępując niektóre związki klasami postaci.
Wersja 10 i 11 (autor: nhahtdh)
Modyfikacja autora polegająca na odrzuceniu wiodącego zera jest nieprawidłowa. Powoduje, że wyrażenia regularne nie pasują do poprawnych liczb, takich jak 1110 (dziesiętny = 14) w przypadku wyrażenia binarnego i 70 w przypadku wyrażenia dziesiętnego. Ta wersja przywraca modyfikację, a tym samym umożliwia dopasowanie dowolnych zer wiodących i pustych ciągów znaków.
Ta wersja zwiększa rozmiar wyrażenia dziesiętnego, ponieważ poprawia błąd w oryginalnym wyrażeniu regularnym, spowodowany brakiem krawędzi (9) od stanu 5 do stanu 3 w oryginalnym DFA.
źródło
1110
, a ten po przecinku nie pasuje70
. Zostało to przetestowane zarówno w Pythonie, jak i Perlu. (python wymagał konwersji co(
do(?:
pierwszej)Wyrażenie regularne .NET,
119118105 bajtów111 znaków nie zezwalających na początkowe 0:
113 znaków nie zezwala na początkowe zera i obsługuje liczby ujemne:
Wypróbuj tutaj.
Objaśnienie (poprzedniej wersji)
Wykorzystuje techniki stosowane przez różne odpowiedzi w tym pytaniu: Cops and Robbers: Reverse Regex Golf . Wyrażenie regularne .NET ma funkcję o nazwie grupa bilansująca, której można używać do wykonywania arytmetyki.
(?<a>)
popycha grupęa
.(?<-a>)
wyświetla to i nie pasuje, jeśli wcześniej nie było grupya
.(?>...)
Dopasuj i nie cofaj się później. Dlatego zawsze będzie pasować tylko do pierwszej dopasowanej alternatywy.((?<-t>)(){3}|){6}
Pomnóż liczbę grupy t przez 3. Zapisz wynik w liczbie grup 2.(?=[1468](?<2>)|)(?=[2569](?<2>){2}|)([3-6](?<2>){3}|\d)
Dopasuj liczbę i tę liczbę z grupy 2.((?<-2>){7}|){3}
Usuń grupę 2 wielokrotnie 7 razy.((?<t-2>)|){6}
Usuń grupę 2 i dopasuj tę samą liczbę do grupy t.$(?(t)a)
Jeśli nadal istnieje dopasowana grupa t, dopasuja
po końcu łańcucha, co jest niemożliwe.Myślałem, że ta 103-bajtowa wersja powinna również działać, ale nie znalazłem obejścia błędu w kompilatorze.
źródło
468 znaków
Smak wyrażenia regularnego Ruby pozwala na rekursję (chociaż jest to rodzaj oszustwa), więc łatwo jest wdrożyć DFA, który rozpoznaje liczby podzielne przez 7 za pomocą tego. Każda nazwana grupa odpowiada stanowi, a każda gałąź na przemian zużywa jedną cyfrę, a następnie przeskakuje do odpowiedniego stanu. Po osiągnięciu końca liczby wyrażenie regularne pasuje tylko wtedy, gdy silnik znajduje się w grupie „A”, w przeciwnym razie nie powiedzie się.
Rozpoznaje zera wiodące.
źródło
{a*b*|a and b an equal amount of times}
)Byłem pod wrażeniem odpowiedzi Griffina i musiałem dowiedzieć się, jak to działa! Wynikiem jest następujący kod JavaScript. (To 3,5k znaków, co jest w pewnym sensie krótsze!)
gen
Funkcja przyjmuje dzielnik i bazę i generuje wyrażenie regularne, które pasuje do liczb w określonej bazie, które są podzielne przez ten dzielnik.Uogólniłem NFA Griffina dla dowolnej bazy:
nfa
funkcja przyjmuje dzielnik i bazę i zwraca dwuwymiarową tablicę przejść. Na przykład wejście wymagane do przejścia ze stanu 0 do stanu 2 tostates[0][2] == "1"
.reduce
Funkcja przyjmuje wstates
tablicy i prowadzi go przez ten algorytm do przetłumaczenia NFA do wyrażenia regularnego. Wygenerowane wyrażenia regularne są ogromne i wyglądają, jakby zawierały wiele zbędnych klauzul, pomimo moich prób optymalizacji. Wyrażenie regularne dla 7 bazowych 10 ma około ~ 67k znaków; Firefox zgłasza „InternalError” dla n> 5 próbując parsować wyrażenie regularne; uruchamianie wyrażenia regularnego w Chrome zaczyna działać powoli dla n> 6.Istnieje również
test
funkcja, która pobiera wyrażenie regularne i bazowe i uruchamia go względem liczb od 0 do 100, więctest(gen(5)) == [0, 5, 10, 15, ...]
.Pomimo nieoptymalnego wyniku, była to fantastyczna okazja do nauki i mam nadzieję, że część tego kodu będzie przydatna w przyszłości!
źródło
Perl / PCRE, 370 znaków
Odrzuca pusty ciąg, a także ciągi z wiodącymi zerami (z wyjątkiem „0”).
źródło