Збіжіть рядки, довжина яких - четверта потужність


28

В рамках цього питання розглянемо лише рядки, що складаються з символів, xповторених довільну кількість разів.

Наприклад:

<empty>
x
xx
xxxxxxxxxxxxxxxx

(Ну, насправді це не повинно бути x- будь-який символ є прекрасним, доки весь рядок має лише 1 тип символів)

Напишіть регулярний вираз у будь-якому смаку регулярного вибору, щоб відповідати всім рядкам, довжина яких n 4 для деякого невід'ємного цілого числа n (n> = 0). Наприклад, рядки довжиною 0, 1, 16, 81 тощо є дійсними; решта недійсні.

Зважаючи на технічні обмеження, значення n, що перевищують 128, важко перевірити. Однак ваш регулярний вираз повинен логічно працювати правильно незалежно.

Зауважте, що вам не дозволяється виконувати довільний код у вашому регулярному виразі (для користувачів Perl). Будь-який інший синтаксис (оглядовий, зворотний посилання тощо) дозволений.

Будь ласка, додайте коротке пояснення щодо вашого підходу до проблеми.

(Будь ласка, не вставте пояснення синтаксису автоматично генерованого регулярного вираження, оскільки вони марні)


"xx" не вірно, чи не так?
Кендалл Фрей

@KendallFrey: Ні. Це не дійсно.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@nhahtdh, ти думаєш, на це можлива відповідь?
xem

1
@Timwi: Так. Java, PCRE (можливо, Perl також, але не можу перевірити), .NET. Шахта не працює в Ruby / JS, хоча.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Це запитання було додано до поширених запитань про регулярне вираження стека в розділі "Розширений Regex-Fu".
aliteralmind

Відповіді:


21

Цей (ір) регулярний вираз, здається, працює.

^((?(1)((?(2)\2((?(3)\3((?(4)\4x{24}|x{60}))|x{50}))|x{15}))|x))*$

Цей регулярний вираз сумісний із ароматами PCRE, Perl, .NET.

Це в основному відповідає «різницевому дереву» (не впевнений, чи є для нього правильна назва), яке повідомляє регулярному вираженню, скільки ще х буде відповідати наступній четвертій потужності:

1     16    81    256   625   1296  2401 ...
   15    65    175   369   671   1105 ...
      50    110   194   302   434 ...
         60    84    108   132 ...
            24    24    24 ...  # the differences level out to 24 on the 4th iteration

\2, \3, \4Магазини і поновлення різниця , як показано на 2 - й, 3 - й і 4 - го рядка, відповідно.

Ця конструкція може бути легко розширена для вищих сил.

Звичайно, це не елегантне рішення, але воно працює.


+1. Чудова відповідь. Хоча ця відповідь відрізняється від моєї (вона використовує умовний регулярний вираз, тоді як моя - ні), вона має такий самий дух, як і моє рішення (експлуатуючи різницеве ​​дерево та використовуючи попередньо оголошений зворотний посилання деяких двигунів регексу).
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

акуратне дерево перегляду різниці. для квадратів дерево дорівнює 1 4 9 16 ... 3 5 7 ... 2 2 2, правда?
Спарр

@Sparr дякую, і так
мінливість

24

Ще одне рішення

Це, на мій погляд, одна з найцікавіших проблем на сайті. Мені потрібно подякувати мертвому коду за те, що він повернувся до вершини.

^((^|xx)(^|\3\4\4)(^|\4x{12})(^x|\1))*$

39 байт , без будь-яких умовних або тверджень ... подібних. Чергування, як вони використовуються ( ^|), є типом умовного способу вибору між "першою ітерацією" та "не першою ітерацією".

Цей регулярний вираз можна побачити тут: http://regex101.com/r/qA5pK3/1

І PCRE, і Python правильно інтерпретують регулярний вираз, і він також був перевірений в Perl до n = 128 , включаючи n 4 -1 і n 4 +1 .


Визначення

Загальна методика така ж, як і в інших розміщених рішеннях: визначте вираз самовідсилки, який на кожній наступній ітерації відповідає довжині, що дорівнює наступному члену функції різниці вперед, D f , з необмеженим кількісним показником ( *). Офіційне визначення функції прямої різниці:

Визначення 1: функція різниці вперед

Крім того, також можуть бути визначені функції вищої різниці порядку:

Визначення 2: друга функція різниці вперед

Або, загалом,:

Визначення 3: kth функція різниці вперед

Функція різниці вперед має масу цікавих властивостей; саме послідовності є похідною для безперервних функцій. Наприклад, D й Апа п - го порядку многочлен завжди буде п-1 - го порядку многочлена, і для будь-якого I , якщо D е я = D е + 1 , то функція F є експоненціальним, багато в чому таким же чином , що похідна e x дорівнює самій собі. Найпростіша дискретна функція, для якої f = D f - 2 n .


f (n) = n 2

Перш ніж вивчити вищезазначене рішення, почнемо з дещо трохи простішого: регулярного вираження, яке відповідає рядкам, довжини яких є ідеальним квадратом. Вивчення функції прямої різниці:

FDF: n ^ 2

Значить, перша ітерація повинна відповідати рядку довжиною 1 , друга - рядок довжиною 3 , третя - рядком довжиною 5 тощо, і взагалі кожна ітерація повинна відповідати рядку на дві довші за попередню. Відповідний регулярний вираз майже безпосередньо випливає з цього твердження:

^(^x|\1xx)*$

Видно, що перша ітерація буде відповідати лише одній x, а кожна наступна ітерація буде відповідати рядку на два довше попередньої, точно так, як зазначено. Це також передбачає напрочуд короткий ідеальний квадратний тест у perl:

(1x$_)=~/^(^1|11\1)*$/

Цей регулярний вираз можна додатково узагальнити, щоб відповідати будь-якій n- гональній довжині:

Трикутні числа:
^(^x|\1x{1})*$

Квадратні цифри:
^(^x|\1x{2})*$

П'ятикутні числа:
^(^x|\1x{3})*$

Шестикутні цифри:
^(^x|\1x{4})*$

тощо.


f (n) = n 3

Переходимо до n 3 , ще раз вивчаючи функцію різниці вперед:

FDF: n ^ 3

Можливо, не відразу зрозуміло, як це здійснити, тому ми також вивчимо другу функцію різниці:

FDF ^ 2: n ^ 3

Отже, функція різниці форвардів збільшується не на постійне, а на лінійне значення. Приємно, що початкове (' -1- е') значення D f 2 дорівнює нулю, що зберігає ініціалізацію на другій ітерації. Отриманий регулярний вираз є наступним:

^((^|\2x{6})(^x|\1))*$

Перша ітерація відповідатиме 1 , як і раніше, друга буде відповідати рядку 6 довше ( 7 ), третя відповідатиме рядку 12 довше ( 19 ) тощо.


f (n) = n 4

Функція різниці вперед для n 4 :

FDF: n ^ 4

Друга функція різниці вперед:

FDF ^ 2: n ^ 4

Третя функція різниці вперед:

FDF ^ 3: n ^ 4

Тепер це некрасиво. Початкові значення для D f 2 і D f 3 є ненульовими, 2 і 12 відповідно, що потрібно буде врахувати. Ви, мабуть, вже зрозуміли, що регулярний вираз буде слідувати цій схемі:

^((^|\2\3{b})(^|\3x{a})(^x|\1))*$

Оскільки D f 3 повинен відповідати довжині 12 на другій ітерації, a обов'язково 12 . Але оскільки вона збільшується на 24 кожного терміну, наступне глибше гніздування повинно використовувати своє попереднє значення вдвічі, маючи на увазі b = 2 . Остаточне, що потрібно зробити - ініціалізувати D f 2 . Оскільки D f 2 впливає на D f безпосередньо, що в кінцевому підсумку ми хочемо співставити, його значення можна ініціалізувати, вставивши відповідний атом безпосередньо в регулярний вираз (^|xx). Остаточним регулярним виразом стає:

^((^|xx)(^|\3\4{2})(^|\4x{12})(^x|\1))*$

Вищі накази

Поліном п’ятого порядку може бути узгоджений із наступним регулярним виразом:
^((^|\2\3{c})(^|\3\4{b})(^|\4x{a})(^x|\1))*$

f (n) = n 5 є досить легким вправою, оскільки початкові значення як для другої, так і для четвертої передових функцій різниці дорівнюють нулю:

^((^|\2\3)(^|\3\4{4})(^|\4x{30})(^x|\1))*$

Для поліномів шести порядку:
^((^|\2\3{d})(^|\3\4{c})(^|\4\5{b})(^|\5x{a})(^x|\1))*$

Для поліномів сьомого порядку:
^((^|\2\3{e})(^|\3\4{d})(^|\4\5{c})(^|\5\6{b})(^|\6x{a})(^x|\1))*$

тощо.

Зауважимо, що не всі поліноми можна зіставити точно таким чином, якщо будь-який з необхідних коефіцієнтів не цілий. Наприклад, n 6 вимагає, щоб a = 60 , b = 8 і c = 3/2 . Це можна вирішити в цьому випадку:

^((^|xx)(^|\3\6\7{2})(^|\4\5)(^|\5\6{2})(^|\6\7{6})(^|\7x{60})(^x|\1))*$

Тут я змінив b на 6 і c на 2 , які мають той самий продукт, що і вищезазначені значення. Важливо, щоб добуток не змінювався, оскільки · b · c ·… керує функцією постійної різниці, яка для поліному шостого порядку D f 6 . Присутні два атома ініціалізації: один для ініціалізації D f до 2 , як при n 4 , а інший для ініціалізації п’ятої різницевої функції до 360 , одночасно додаючи відсутні два з b .


На яких двигунах ви перевірили це?
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Я нарешті розумію, що відбувається. Справді, єдине, що потрібно, - це підтримка для прямого посилання. +1
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@nhahtdh ах, ти маєш рацію. Переадресація також не обов'язково є універсальною ознакою.
примо

1
Відмінно! Я люблю, як це коротко, просто і легко зрозуміти. Маючи неглибоке гніздування, легко вручну порахувати, як він буде вести себе. Крім того, він настільки ж швидкий, як рішення про волатильність і nhahtdh . Мені подобається ваше детальне пояснення, включаючи демонстрацію того, що це можна поширити навіть на многочлени. Я б дав бонусні бали, якби міг.
Мертвий код

@Lynn дякую! Не очікував, що ...
прим

13

Ось рішення, яке не використовує умовні умови, попередньо оголошені або вкладені зворотні посилання, відсталі, балансуючі групи або регекс. Він використовує лише lookahead та стандартні зворотні налаштування, які дуже широко підтримуються. Мене надихнуло діяти під цими обмеженнями завдяки Regex Golf , який використовує регекс-двигун ECMAScript.

Те, як працює цей 50-байтний регулярний вираз, концептуально досить простий і зовсім інший, ніж усі інші представлені рішення цієї головоломки. Дивовижно було виявити, що подібний тип математичної логіки виражається в регулярному вираженні.

      \2                     \4  \5
^((?=(xx+?)\2+$)((?=\2+$)(?=(x+)(\4+)$)\5){4})*x?$

(Групи захоплення позначені над регулярними виразами)

Регулярний вираз може бути узагальнена на будь-якої потужності , просто шляхом заміни 4ін {4}з необхідною потужністю.

Спробуйте в Інтернеті!

Він працює шляхом багаторазового ділення найменшої четвертої сили простого числа, на яку поточне значення ділиться на. Оскільки такий коефіцієнт на кожному кроці - це завжди четверта потужність, якщо початкове значення було четвертим. Кінцевий коефіцієнт 1 вказує на те, що початкове значення справді було четвертим значенням; цим завершується матч. Нуль також відповідає.

По-перше, вона використовує групу лінивого захоплення, \2щоб захопити найменший коефіцієнт числа, більший за 1. Як такий, цей фактор гарантовано є основним. Наприклад, з 1296 (6 ^ 4) він спочатку захопить \2= 2.

Потім на початку циклу, який повторюється 4 рази, він перевіряє, чи поточне число ділиться на \2, з (?=\2+$). Перший раз через цю петлю цей тест марний, але його призначення стане очевидним пізніше.

Потім всередині цього внутрішнього контуру, він використовує жадібну групу захоплення , \4щоб захопити найбільший фактор цього числа менше , ніж він сам: (?=(x+)(\4+)$). Фактично це ділить число на його найменший простий коефіцієнт \2; наприклад, 1296 спочатку буде фіксуватися як \4= 1296/2 = 648. Зауважимо, що ділення поточного числа на \2неявне. Хоча можна чітко розділити поточне число на число, що міститься в групі захоплення (яке я виявив лише через чотири дні після опублікування цієї відповіді), але це зробить більш повільний і важче зрозумілий регулярний вираз, і це не так необхідно, оскільки найменший коефіцієнт числа, більший за 1, завжди буде відповідати його найбільшому коефіцієнту, меншому від самого себе (таким, що їх добуток дорівнює самому числу).

Оскільки цей вид регулярних виразів може лише "з'їсти" рядок (зробивши її меншою), залишивши результат у кінці рядка, нам потрібно "перемістити" результат поділу до кінця рядка. Це робиться шляхом фіксації результату віднімання (поточне число мінус \4) у групу захоплення \5, а потім, поза межами пошуку, узгодження частини початку поточного числа, що відповідає \5. Залишається залишилася необроблена рядок у кінці, що відповідає \4довжині.

Тепер він замикається на початку внутрішньої петлі, де стає очевидним, чому існує тест на подільність за простим фактором. Ми щойно розділили найменший простий коефіцієнт числа; якщо число все ще ділиться на цей множник, це означає, що вихідне число може бути ділене на четверту силу цього множника. Перший раз, коли цей тест робиться, він марний, але наступні 3 рази він визначає, чи результат неявного поділу на \2все ще ділиться на \2. Якщо вона ще ділиться \2на початку кожної ітерації циклу, то це доводить, що кожна ітерація ділила число на \2.

У нашому прикладі з входом 1296 це буде проходити наступним чином:

\2= 2
\4= 1296/2 = 648
\4= 648/2 = 324
\4= 324/2 = 162
\4= 162/2 = 81

Тепер регулярний вираз може повернутись до першого кроку; ось що *робить фінал . У цьому прикладі 81 стане новим числом; наступний цикл піде наступним чином:

\2= 3
\4= 81/3 = 27
\4= 27/3 = 9
\4= 9/3 = 3
\4= 3/3 = 1

Тепер він знову повернеться до першого кроку, з новим номером 1.

Число 1 не може бути поділене на будь-який простий, що зробить його невідповідним (?=(xx+?)\2+$), тому він виходить з циклу верхнього рівня (той, що *знаходиться в кінці). Зараз воно доходить до x?$. Це може відповідати лише нулю або одному. Поточне число в цей момент буде 0 або 1, якщо і тільки тоді, коли початкове число було ідеальною четвертою потужністю; якщо в цій точці це 0, це означає, що цикл верхнього рівня ніколи нічим не відповідав, і якщо він дорівнює 1, це означає, що цикл верхнього рівня розділив ідеальну четверту потужність вниз, поки її вже нічим не поділили (або в першу чергу це було 1, тобто цикл верхнього рівня ніколи нічого не відповідав).

Це також можна вирішити в 49 байтах, повторивши явний поділ (який також узагальнено для всіх повноважень - замініть бажану потужність мінус одну на " {3}), але цей метод набагато повільніше, і пояснення алгоритму, який він використовує виходить за рамки цього відповіді:

^((x+)((\2(x+))(?=(\4*)\2*$)\4*(?=\5$\6)){3})?x?$

Спробуйте в Інтернеті!


З мого тестування (до довжини 1024), здається, що це правильно. Однак регулярний вираз є занадто повільним - це вимагає багато часу, щоб відповідати довжині 16 ^ 4, тому це важко перевірити на велику кількість. Але оскільки продуктивність не потрібна, я підкажу, коли я зрозумію ваш регекс.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Регекс та мінливість є приголомшливими. Їх швидкість і стислість мене вражають, і вони обидва відповідають 100000000 за 7,5 секунди на моєму i7-2600k, набагато швидше, ніж я б очікував, що буде регулярний вираз. Моє рішення тут на зовсім іншому порядку, тому що потрібно 50 секунд, щоб відповідати 50625. Але мета з моєю була не швидкість, а, швидше, виконання завдання з мінімальною довжиною коду, використовуючи набагато більш обмежений набір операцій.
Deadcode

Наші відповіді швидкі, оскільки вони ледь не роблять жодного зворотнього огляду. Ви робите багато зворотних тренувань ((((x+)\5+)\4+)\3+)\2+$. Твій також по-своєму дивовижний, тому що я навіть не можу думати, як співставити квадратне число без оголошеного вперед зворотного зв'язку.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

До речі, це питання не кодовий гольф, а загадка. Я не суджу рішення за довжиною коду.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Ой. Це пояснює, чому ви використовували (?:). Тож чи слід редагувати свою відповідь, щоб оптимізована версія стала первинною?
Мертвий код

8

Рішення

^(?:(?=(^|(?<=^x)x|xx\1))(?=(^|\1\2))(^x|\3\2{12}xx))*$

Цей регулярний вираз сумісний із ароматами Java, Perl, PCRE та .NET. Цей регулярний вираз використовує цілий ряд функцій: погляд вперед, погляд назад і декларований вперед зворотний посилання. Заздалегідь оголошені типи зворотного посилання обмежують сумісність цього регулярного виразу на декілька двигунів.

Пояснення

Це рішення використовує наступне виведення.

Повністю розширивши підсумки, ми можемо довести таку рівність:

\ sum \ limit_ {i = 1} ^ n (i + 1) ^ 4 - \ сума \ межі_ {i = 1} ^ ni ^ 4 = (n + 1) ^ 4 - 1
\ сума \ межі_ {i = 1} ^ ni ^ 4 - \ сума \ межі_ {i = 1} ^ n (i-1) ^ 4 = n ^ 4

З’єднаємо підсумок з лівого боку:

\ сума \ межі_ {i = 1} ^ n (4 (i + 1) ^ 3 - 6 (i + 1) ^ 2 + 4 (i + 1) - 1) = (n + 1) ^ 4 - 1
\ сума \ межі_ {i = 1} ^ n (4i ^ 3 - 6i ^ 2 + 4i - 1) = n ^ 4

Відніміть 2 рівняння (верхнє рівняння мінус нижнє рівняння), а потім об'єднайте підсумки з лівої сторони, а потім спростіть:

\ sum \ limit_ {i = 1} ^ n (12i ^ 2 + 2) = (n + 1) ^ 4 - n ^ 4 - 1

Ми отримуємо різницю між послідовними четвертими потужностями як сумою потужності:

(n + 1) ^ 4 - n ^ 4 = \ сума \ межі_ {i = 1} ^ n (12i ^ 2 + 2) + 1

Це означає, що різниця між послідовними четвертими потужностями збільшиться на (12n 2 + 2).

Щоб полегшити думку, посилаючись на дерево різниці у відповіді на Волатильність :

  • Права частина остаточного рівняння - це другий рядок у дереві різниці.
  • Приріст (12n 2 + 2) - це 3-й ряд у дереві різниці.

Досить математики. Повернутися до рішення вище:

  • 1-я група захоплення підтримує ряд непарних чисел для обчислення i 2, як видно з рівняння.

    Точно кажучи, довжина 1-ї групи захоплення становитиме 0 (невикористаних), 1, 3, 5, 7, ... у міру ітерації циклу.

    (?<=^x)xвстановлює початкове значення для ряду непарних чисел. ^Тільки там , щоб дозволити упереджувальний бути задоволені в першій ітерації.

    xx\1 додає 2 і перехід до наступного непарного числа.

  • 2-а група захоплення підтримує ряд квадратних чисел для i 2 .

    Точно кажучи, довжина 2-ї групи захоплення становитиме 0, 1, 4, 9, ... у міру ітерації петлі.

    ^в (^|\1\2)задає початкове значення для рядів квадратних чисел. І \1\2додає непарне число до поточного квадратного числа, щоб перейти до наступного числа квадрата.

  • Третя група захоплення (поза будь-яким поглядом вперед і фактично споживає текст) відповідає всій правій частині рівняння, яке ми отримали вище.

    ^xв (^x|\3\2{12}xx)задає початкове значення, яке є + 1правою частиною рівняння.

    \3\2{12}xxдодає збільшення різниці (12n 2 + 2), використовуючи n 2 з групи захоплення 2, і збігайте різницю одночасно.

Таке розташування можливо завдяки тому, що кількість тексту, відповідного кожній ітерації, більше або дорівнює кількості тексту, необхідного для виконання перспективного побудови n 2 .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.