ECMAScript Regex, 733+ 690+ 158 119 118 (117🐌) байтов
Мой интерес к регулярным выражениям возник с новой силой после более чем 4,5 лет бездействия. Поэтому я отправился на поиски более естественных наборов чисел и функций, которые соответствовали бы унарным регулярным выражениям ECMAScript, возобновил совершенствование своего механизма регулярных выражений и также начал совершенствовать PCRE.
Я очарован чуждостью построения математических функций в регулярном выражении ECMAScript. К проблемам следует подходить с совершенно иной точки зрения, и до момента появления ключевого понимания неизвестно, решаются ли они вообще. Это заставляет создавать гораздо более широкую сеть, чтобы определить, какие математические свойства могут быть использованы для решения конкретной задачи.
Сопоставление факторных чисел было проблемой, которую я даже не рассматривал в 2014 году - или, если бы я сделал, только на мгновение, отклонил бы ее как слишком маловероятную, чтобы быть возможной. Но в прошлом месяце я понял, что это можно сделать.
Как и в других моих статьях по регулярным выражениям ECMA, я дам предупреждение: я настоятельно рекомендую научиться решать унарные математические задачи в регулярных выражениях ECMAScript. Для меня это было увлекательное путешествие, и я не хочу испортить его тем, кто потенциально может попробовать его сами, особенно тем, кто интересуется теорией чисел. См. Этот более ранний пост для списка последовательно рекомендованных спойлером рекомендованных проблем, чтобы решить одну за другой.
Так что не читайте дальше, если вы не хотите, чтобы какая-то продвинутая магия унарных регулярных выражений была испорчена для вас . Если вы действительно хотите сами разобраться в этой магии, я настоятельно рекомендую начать с решения некоторых проблем в регулярном выражении ECMAScript, как описано в этом посте, связанном выше.
Это была моя идея:
Проблема с сопоставлением этого набора номеров, как и с большинством других, заключается в том, что в ECMA обычно невозможно отслеживать два меняющихся числа в цикле. Иногда они могут быть мультиплексированы (например, полномочия одной и той же базы могут быть добавлены вместе однозначно), но это зависит от их свойств. Поэтому я не мог просто начать с входного числа и делить его на постепенно увеличивающиеся дивиденды, пока не достиг 1 (или я так думал, по крайней мере).
Затем я провел небольшое исследование множителей простых факторов в факторных числах и узнал, что для этого есть формула , которую я мог бы реализовать в регулярном выражении ECMA!
После того, как я немного поработал над этим и тем временем создал несколько других регулярных выражений, я решил написать факториальное регулярное выражение. Это заняло несколько часов, но в итоге все заработало. В качестве дополнительного бонуса алгоритм может возвращать обратный факториал как совпадение. Этого даже не избежать; по самой природе того, как это должно быть реализовано в ECMA, необходимо сделать предположение о том, что такое обратный факториал, прежде чем делать что-либо еще.
Недостатком было то, что этот алгоритм был создан для очень длинного регулярного выражения ... но я был рад, что в итоге ему потребовалась методика, используемая в моем регулярном выражении умножения 651 байт (которое оказалось устаревшим, потому что другой метод сделал для 50 байт регулярное выражение). Я надеялся, что возникнет проблема, требующая этого трюка: работа с двумя числами, которые являются степенями одной и той же базы, в цикле, путем однозначного их сложения и разделения на каждой итерации.
Но из-за сложности и длины этого алгоритма я использовал молекулярные взгляды (формы (?*...)
) для его реализации. Это особенность не в ECMAScript или любом другом распространенном движке регулярных выражений, а та, которую я реализовал в своем движке . Без каких-либо захватов внутри молекулярного обзора он функционально эквивалентен атомному прогнозированию, но с захватами он может быть очень мощным. Движок вернется назад, и это может быть использовано для предположения значения, которое циклически перебирает все возможности (для последующего тестирования) без использования символов ввода. Их использование может сделать реализацию более чистой. (Внешний вид переменной длины по меньшей мере равен по мощности молекулярному взгляду, но последний имеет тенденцию создавать более прямые и элегантные реализации.)
Таким образом, длины в 733 и 690 байтов фактически не представляют ECMAScript-совместимые воплощения решения - отсюда и «+» после них; Конечно, возможно перенести этот алгоритм на чистый ECMAScript (который немного увеличит его длину), но я не смог обойти это ... потому что я подумал о гораздо более простом и более компактном алгоритме! Тот, который может быть легко реализован без молекулярных взглядов. Это также значительно быстрее.
Этот новый, как и предыдущий, должен угадать обратный факториал, перебирая все возможности и проверяя их на совпадение. Он делит N на 2, чтобы освободить место для работы, которую он должен выполнить, а затем запускает цикл, в котором он многократно делит входные данные на делитель, который начинается с 3 и увеличивается каждый раз. (Таким образом, 1! И 2! Не могут быть сопоставлены основным алгоритмом, и должны рассматриваться отдельно.) Делитель отслеживается путем добавления его к коэффициенту выполнения; эти два числа могут быть однозначно разделены, потому что, предполагая M! == N, текущий коэффициент будет делиться на M, пока он не станет равен M.
Это регулярное выражение выполняет деление на переменную во внутренней части цикла. Алгоритм деления такой же, как и в других моих регулярных выражениях (и похож на алгоритм умножения): для A≤B, A * B = C, если есть, только если C% A = 0 и B - наибольшее число, удовлетворяющее B≤C и C% B = 0 и (CB- (A-1))% (B-1) = 0, где C - дивиденд, A - делитель, а B - частное. (Аналогичный алгоритм может быть использован для случая, когда A≥B, и если неизвестно, как A сравнивается с B, все, что нужно, - это один дополнительный тест делимости).
Поэтому мне нравится, что эту проблему удалось снизить до еще меньшей сложности, чем мой регулярный код Фибоначчи, оптимизированный для гольфа , но я вздыхаю с разочарованием, что моей технике мультиплексирования «силы одного и того же основания» придется ждать еще одной проблемы это на самом деле требует этого, потому что это не так. Это история моего 651-байтового алгоритма умножения, замененного 50-байтовым, снова и снова!
Редактировать: мне удалось отбросить 1 байт (119 → 118) с помощью трюка, найденного Грими, который может дополнительно сократить деление в случае, когда коэффициент гарантированно будет больше или равен делителю.
Без дальнейших церемоний, вот регулярное выражение:
Верная / ложная версия (118 байт):
^((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\3$|^xx?$
Попробуйте онлайн!
Возвращает обратный факториал или несоответствие (124 байта):
^(?=((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\3$)\3|^xx?$
Попробуйте онлайн!
Возврат обратного факториала или отсутствие совпадения в ECMAScript +\K
(120 байт):
^((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\K\3$|^xx?$
И свободная версия с комментариями:
^
(?= # Remove this lookahead and the \3 following it, while
# preserving its contents unchanged, to get a 119 byte
# regex that only returns match / no-match.
((x*)x*)(?=\1$) # Assert that tail is even; \1 = tail / 2;
# \2 = (conjectured N for which tail == N!)-3; tail = \1
(?=(xxx\2)+$) # \3 = \2+3 == N; Assert that tail is divisible by \3
# The loop is seeded: X = \1; I = 3; tail = X + I-3
(
(?=\2\3*(x(?!\3)xx(x*))) # \5 = I; \6 = I-3; Assert that \5 <= \3
\6 # tail = X
(?=\5+$) # Assert that tail is divisible by \5
(?=
( # \7 = tail / \5
(x*) # \8 = \7-1
(?=\5(\8*$)) # \9 = tool for making tail = \5\8
x
)
\7*$
)
x\9 # Prepare the next iteration of the loop: X = \7; I += 1;
# tail = X + I-3
(?=x\6\3+$) # Assert that \7 is divisible by \3
)*
\2\3$
)
\3 # Return N, the inverse factorial, as a match
|
^xx?$ # Match 1 and 2, which the main algorithm can't handle
Полная история моих оптимизаций по гольфу этих регулярных выражений находится на github:
регулярное выражение для сопоставления факторных чисел - метод сравнения множественности, с молекулярным lookahead.txt
регулярное выражение для сопоставления факториальных чисел ( текст, показанный выше)
((x*)x*)
((x*)+)
((x+)+)
п = 3 !\2
3 - 3 = 0
Механизм регулярных выражений .NET не эмулирует это поведение в режиме ECMAScript, и, таким образом, работает 117-байтовое регулярное выражение:
Попробуйте онлайн! (версия с экспоненциальным замедлением, с механизмом регулярных выражений .NET + эмуляция ECMAScript)
1
?