Регекс (аромат 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 є ідеальним квадратом (тому для деяких n повинен бути F n-1 ).
- Стверджуємо, що або 5b 2 + 4, або 5b 2 + 4 є ідеальним квадратом (тому b має бути F n ).
- Перевірте, що z = F 2n + 3 або z = F 2n + 4 , використовуючи попередньо побудовані 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
регулярний вираз для відповідності чисел Фібоначчі - найшвидший.txt регекс
для відповідності чисел Фібоначчі - повернення index.txt