Regex (на ECMAScript), 392 358 328 224 206 165 байтов
Методы, которые необходимо использовать для сопоставления чисел Фибоначчи с регулярным выражением ECMAScript (в унарном формате), далеки от того, как это лучше всего сделать в большинстве других разновидностей регулярных выражений. Отсутствие прямых / вложенных обратных ссылок или рекурсии означает, что невозможно напрямую подсчитать или сохранить промежуточный итог чего-либо. Отсутствие осмотрительности часто затрудняет работу даже при наличии достаточного пространства для работы.
К многим проблемам нужно подходить с совершенно иной точки зрения, и они кажутся неразрешимыми до появления какой-то ключевой идеи. Это заставляет вас создавать более широкую сеть, чтобы определить, какие математические свойства чисел, с которыми вы работаете, могут быть использованы для решения конкретной задачи.
В марте 2014 года это произошло с числами Фибоначчи. Глядя на страницу Википедии, я изначально не мог найти выход, хотя одно конкретное свойство казалось мучительно близким. Затем математик Теукон описал метод, который дал понять, что это возможно сделать, используя это свойство вместе с другим. Он не хотел на самом деле построить регулярное выражение. Его реакция, когда я пошел вперед и сделал это:
Ты сумасшедший! ... Я думал, что вы могли бы сделать это.
Как и в других моих постах по унарным математическим регулярным выражениям в ECMAScript, я дам предупреждение: я настоятельно рекомендую узнать, как решать унарные математические задачи в регулярных выражениях в ECMAScript. Для меня это было увлекательное путешествие, и я не хочу испортить его тем, кто потенциально может попробовать его сами, особенно тем, кто интересуется теорией чисел. См. Этот пост для списка последовательно рекомендованных спойлером проблем, решаемых один за другим.
Так что не читайте дальше, если вы не хотите, чтобы какая-то магия унарного выражения была испорчена для вас . Если вы действительно хотите сами разобраться в этой магии, я настоятельно рекомендую начать с решения некоторых проблем в регулярном выражении ECMAScript, как описано в этом посте, связанном выше.
Задача, с которой я изначально столкнулся: положительное целое число x - это число Фибоначчи тогда и только тогда, когда 5x 2 + 4 и / или 5x 2 - 4 - идеальный квадрат. Но нет места, чтобы вычислить это в регулярном выражении. Единственное пространство, в котором мы должны работать, это само число. У нас даже недостаточно места, чтобы умножить на 5 или взять квадрат, не говоря уже о обоих.
Идея teukon о том, как ее решить ( изначально размещена здесь ):
Регулярное выражение представлено строкой вида ^x*$
, пусть z будет его длиной. Проверьте, является ли z одним из первых нескольких чисел Фибоначчи от руки (до 21 должно подойти). Если это не так:
- Прочитайте пару чисел a <b, чтобы b было не больше 2a.
- Используйте прогнозные прогнозы для построения 2 , ab и b 2 .
- Утвердите, что либо 5a 2 + 4, либо 5a 2 - 4 - идеальный квадрат (поэтому a должно быть F n-1 для некоторого n).
- Утвердите, что либо 5b 2 + 4, либо 5b 2 + 4 - идеальный квадрат (поэтому b должно быть F n ).
- Проверьте, что z = F 2n + 3 или z = F 2n + 4 , используя ранее построенные a 2 , ab и b 2 и тождества:
- F 2n-1 = F n 2 + F n-1 2
- F 2n = (2F n-1 + F n ) F n
Вкратце: эти тождества позволяют нам уменьшить проблему проверки того, что заданное число - это число Фибоначчи, до проверки того, что пара гораздо меньших чисел - это число Фибоначчи. Маленькая алгебра покажет, что для достаточно большого n (n = 3 должно), F 2n + 3 > F n + 5F n 2 + 4, поэтому всегда должно быть достаточно места.
А вот макет алгоритма на C, который я написал в качестве теста перед его реализацией в регулярном выражении.
Так что без дальнейших церемоний, вот регулярное выражение:
^((?=(x*).*(?=x{4}(x{5}(\2{5}))(?=\3*$)\4+$)(|x{4})(?=xx(x*)(\6x?))\5(x(x*))(?=(\8*)\9+$)(?=\8*$\10)\8*(?=(x\2\9+$))(x*)\12)\7\11(\6\11|\12)|x{0,3}|x{5}|x{8}|x{21})$
Попробуйте онлайн!
И довольно напечатанная, закомментированная версия:
^(
(?=
(x*) # \2+1 = potential number for which 5*(\2+1)^2 ± 4
# is a perfect square; this is true iff \2+1 is a Fibonacci
# number. Outside the surrounding lookahead block, \2+1 is
# guaranteed to be the largest number for which this is true
# such that \2 + 5*(\2+1)^2 + 4 fits into the main number.
.*
(?= # tail = (\2+1) * (\2+1) * 5 + 4
x{4}
( # \3 = (\2+1) * 5
x{5}
(\2{5}) # \4 = \2 * 5
)
(?=\3*$)
\4+$
)
(|x{4}) # \5 = parity - determined by whether the index of Fibonacci
# number \2+1 is odd or even
(?=xx (x*)(\6 x?)) # \6 = arithmetic mean of (\2+1) * (\2+1) * 5 and \8 * \8,
# divided by 2
# \7 = the other half, including remainder
\5
# require that the current tail is a perfect square
(x(x*)) # \8 = potential square root, which will be the square root
# outside the surrounding lookahead; \9 = \8-1
(?=(\8*)\9+$) # \10 = must be zero for \8 to be a valid square root
(?=\8*$\10)
\8*
(?=(x\2\9+$)) # \11 = result of multiplying \8 * (\2+1), where \8 is larger
(x*)\12 # \12 = \11 / 2; the remainder will always be the same as it
# is in \7, because \8 is odd iff \2+1 is odd
)
\7\11
(
\6\11
|
\12
)
|
x{0,3}|x{5}|x{8}|x{21} # The Fibonacci numbers 0, 1, 2, 3, 5, 8, 21 cannot be handled
# by our main algorithm, so match them here; note, as it so
# happens the main algorithm does match 13, so that doesn't
# need to be handled here.
)$
Алгоритм умножения не объясняется в этих комментариях, но вкратце объясняется в абзаце моего регулярного выражения с многочисленными числами .
Я поддерживал шесть различных версий регулярного выражения Фибоначчи: четыре, которые ускоряют от самой короткой длины до самой быстрой скорости и используют алгоритм, описанный выше, и две другие, которые используют другой, намного более быстрый, но гораздо более длинный алгоритм, который, как я обнаружил, может на самом деле вернуть индекс Фибоначчи как совпадение (объяснение того, что алгоритм здесь выходит за рамки этого поста, но объясняется в оригинальном обсуждении Gist ). Я не думаю, что снова поддержу столько очень похожих версий регулярного выражения, потому что в то время я проводил все свои тесты в PCRE и Perl, но мой движок регулярных выражений достаточно быстрый, так что проблемы со скоростью уже не так важны (и если конкретная конструкция вызывает узкое место, я могу добавить оптимизацию для нее) - хотя я, вероятно, снова поддержу одну самую быструю версию и одну самую короткую версию, если разница в скорости были достаточно большими.
Версия «верните индекс Фибоначчи минус 1 как матч» (не в гольфе):
Попробуйте онлайн!
Все версии находятся на github с полной историей коммитов оптимизации гольфа:
регулярное выражение для сопоставления чисел Фибоначчи - короткий, скорость 0.txt (самый короткий, но самый медленный, как в этом посте)
регулярное выражение для сопоставления чисел Фибоначчи - короткий, скорость 1.txt
регулярное выражение для сопоставления чисел Фибоначчи - короткий, скорость 2.txt
регулярное выражение для сопоставление чисел Фибоначчи - короткие, скоростные регулярные
выражения 3.txt для сопоставления чисел Фибоначчи - регулярное выражение fastest.txt
для сопоставления чисел Фибоначчи - возвращение index.txt