ECMAScript Regex, 733+ 690+ 158 119 118 (117🐌) байт
Мій інтерес до регексу був викликаний новою силою після більш ніж 4½ років бездіяльності. Таким чином, я пішов шукати більш природні набори чисел та функції, які б відповідали одинарним регулярним виразам ECMAScript, відновив вдосконалення моєї системи регексу, а також почав чистити на PCRE.
Мене захоплює чужість побудови математичних функцій в регулярному вираженні ECMAScript. До проблем слід підходити з зовсім іншого погляду, і до появи ключового розуміння невідомо, чи вони взагалі вирішуються. Це змушує закидати набагато ширшу мережу, щоб знайти, які математичні властивості можуть бути використані для вирішення конкретної проблеми.
Узгодження фактичних чисел було проблемою, яку я навіть не вважав вирішенням у 2014 році - або, якщо це зробити, лише на мить, відкинувши це як надто малоймовірне, що можливо. Але минулого місяця я зрозумів, що це можна зробити.
Як і в інших моїх публікаціях з регулярних виразів ECMA, я надам попередження: настійно рекомендую навчитися розв’язувати одинарні математичні завдання в регулярному вираженні ECMAScript. Це була захоплююча подорож для мене, і я не хочу її зіпсувати нікому, хто, можливо, захоче спробувати його сам, особливо тим, хто цікавиться теорією чисел. Перегляньте це попереднє повідомлення, щоб ознайомитися зі списком рекомендованих послідовно позначених спойлерами проблем, які потрібно вирішити одна за одною.
Тож не читайте більше, якщо ви не хочете, щоб якась просунута онірна регекс-магія зіпсована для вас . Якщо ви дійсно хочете зняти цю магію самостійно, я настійно рекомендую почати з вирішення деяких проблем у регулярному вираженні ECMAScript, як описано у цій публікації, що пов’язана вище.
Така моя ідея:
Проблема зі збігом цього набору чисел, як і у більшості інших, полягає в тому, що в ECMA зазвичай неможливо відслідковувати два зміни числа в циклі. Іноді вони можуть бути мультиплексовані (наприклад, потужності однієї бази можна однозначно додавати), але це залежить від їх властивостей. Тож я не міг просто почати з вхідного числа, а поділити його на дивіденд з наростаючим збільшенням до досягнення рівня 1 (чи так я думав, принаймні).
Тоді я провів кілька досліджень щодо множинності простих факторів у фактичних числах і дізнався, що для цього є формула - і це, можливо, я міг би реалізувати в регулярному виразі ECMA!
Потрапивши на нього деякий час і побудувавши деякі інші регекси тим часом, я взявся за завдання написання факторного регексу. Це пройшло кілька годин, але закінчилось непогано. Як додатковий бонус, алгоритм міг повернути обернений фактор як збіг. Уникнути цього навіть не було; за самою природою того, як це має бути впроваджено в ECMA, необхідно здогадатися, що таке зворотний факторіал, перш ніж робити щось інше.
Мінус полягав у тому, що цей алгоритм створив дуже тривалий регулярний вираз ... але я був задоволений тим, що в кінцевому підсумку потрібна техніка, що використовується в моєму регексері множення 651 байтів (той, який у кінцевому підсумку застарів, бо інший метод зроблений для 50 байт-регекс). Я сподівався, що з’явиться проблема, що вимагає цього фокусу: Операція на двох числах, що є обома повноваженнями однієї бази, в циклі, додаючи їх однозначно і розділяючи їх при кожній ітерації.
Але через складність та тривалість цього алгоритму я використовував молекулярні динаміки пошуку (форми (?*...)
) для його реалізації. Це особливість, не в ECMAScript або будь-якому іншому двигуні основного регулярного виразів, а в тій, яку я реалізував у своєму двигуні . Без будь-яких захоплень всередині молекулярної вершини, він функціонально еквівалентний атомному монголові, але при захопленні він може бути дуже потужним. Двигун буде відкликатись у головний ряд, і це можна використовувати для здогадки значення, яке переходить усі можливості (для подальшого тестування), не витрачаючи символів вводу. Використання їх може значно покращити реалізацію. (Попереду змінної довжини, як мінімум, рівний за потужністю молекулярний вигляд, але останні мають тенденцію до більш простого та елегантного втілення.)
Отже, довжина 733 та 690 байт насправді не представляє сумісні з ECMAScript втілення рішення - отже, "+" після них; це, безумовно, можливо, щоб перенести цей алгоритм до чистого ECMAScript (що би збільшило його довжину зовсім небагато), але я не обійшов його ... тому що я думав про набагато простіший і компактніший алгоритм! Той, який можна було б легко реалізувати без молекулярних лукаголов. Це також значно швидше.
Цей новий, як і попередній, повинен здогадуватися на зворотному факторіалі, перебираючи всі можливості та перевіряючи їх на відповідність. Він ділить N на 2, щоб звільнити роботу, яку йому потрібно виконати, а потім наділяє цикл, в якому він буде неодноразово ділити вхід дільником, що починається на 3 та з кроком щоразу. (Таким чином, 1! І 2! Не можуть бути узгоджені з основним алгоритмом, і їх слід вирішувати окремо.) Дільник відстежується, додаючи його до діючого коефіцієнта; ці два числа можна однозначно розділити, оскільки, припускаючи М! == 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:
регулярний вирівнювання для збігу фактичних чисел - метод порівняння кратності, з молекулярним reox
lookahead.txt для відповідності фактичних чисел.txt (показаний вище)
((x*)x*)
((x*)+)
((x+)+)
n = 3 !\2
3 - 3 = 0
Режим двигуна .NET не імітує цю поведінку в режимі ECMAScript, і, таким чином, 117-байтний регулярний вираз працює:
Спробуйте в Інтернеті! (версія експоненціального сповільнення, з двигуном .ge regex + емуляція ECMAScript)
1
?