Сітківка , 66 63 45 43 36 байт
^()(\1(?<1>.\1))+(\1(.(?(4).\4)))*$
Незважаючи на заголовок Retina, це лише звичайний .NET-регулярний вираз, який приймає унарні подання лескіанських чисел.
Входи 999 і 1000 проходять за секунду.
Спробуйте в Інтернеті! (Перший рядок включає тестовий набір, відокремлений підведенням по каналах, а наступні два дбають про перетворення в одинарне для зручності.)
Пояснення
Рішення засноване на класифікації, що вхід може бути записаний як i*i + j*(i + j)
позитивний, так i
і негативний j
(оскільки нам не потрібно обробляти введення 0
), і n*n
це лише сума перших n
непарних цілих чисел. Гольф це було цікавою вправою в прямому посиланні.
"Посилання в прямому напрямку" - це коли ви ставите зворотний зв'язок всередині групи, на яку він посилається. Звичайно, це не спрацьовує при використанні групи вперше, оскільки ще немає нічого, що має бути зворотним референдумом, але якщо ви помістите це в цикл, то backreference отримує попередню ітерацію щоразу. Це, в свою чергу, дозволить вам створити більшу кількість знімків з кожною ітерацією. Це можна використовувати для створення дуже компактних візерунків для таких речей, як трикутні числа, квадрати та числа Фібоначчі.
Як приклад, використовуючи той факт, що квадрати - це лише суми перших n
непарних цілих чисел, ми можемо зіставити такий квадратний вхід:
(^.|..\1)+$
На першій ітерації ..\1
не можна працювати, оскільки \1
ще не має значення. Отже, ми почнемо з того ^.
, що захоплює одного символу в групу 1
. На наступних ітераціях ^.
більше не збігається через якір, але тепер ..\1
діє. Він відповідає двом символам, ніж попередня ітерація, і оновлює зйомку. Таким чином ми співставляємо збільшення непарних чисел, отримуючи квадрат після кожної ітерації.
На жаль, ми не можемо використовувати цю техніку як є. Після відповідності i*i
нам також потрібно отримати i
, щоб ми могли помножити його на j
. Простий (але довгий) спосіб зробити це - скористатися тим фактом, що відповідність i*i
бере i
ітерації, щоб ми захопили i
речі в групі 1
. Зараз ми могли використовувати балансуючі групи для отримання цього i
, але, як я сказав, це дорого.
Натомість я придумав інший спосіб записати цю "суму послідовних непарних цілих чисел", яка також поступається i
в групі захоплення наприкінці. Звичайно, i
непарне число просто 2i-1
. Це дає нам можливість збільшити пряму посилання лише на 1 на кожну ітерацію. Ось ця частина:
^()(\1(?<1>.\1))+
Це ()
просто підштовхує порожнє захоплення до групи 1
(ініціалізація i
до 0
). Це майже еквівалентно ^.|
простому рішенню вище, але використання |
в цьому випадку було б трохи складніше.
Тоді у нас є основна петля (\1(?<1>.\1))
. \1
відповідає попередньому i
, а (?<1>.\1)
потім оновлює групу 1
з i+1
. З точки зору нового i
, ми щойно відповідали 2i-1
персонажам. Саме те, що нам потрібно.
Коли ми закінчимо, ми зіставили деякий квадрат i*i
і група 1
все ще містить i
символи.
Друга частина ближче до простого квадратного зіставлення, яке я показав вище. Давайте поки ігноруємо зворотну посилання на 1
:
(.(?(4).\1))*
Це в основному те саме, що (^.|..\4)*
, за винятком того, що ми не можемо скористатись ^
тим, що ми не на початку рядка. Замість цього ми використовуємо умовне, щоб відповідати додатковому .\1
лише тоді, коли ми вже використовували групу 4
. Але насправді це точно так само. Це нам дає j*j
.
Єдине, чого не вистачає - це j*i
термін. Ми поєднуємо це з тим j*j
, що використовуємо той факт, що j*j
обчислення все ще приймають j
ітерації. Тож для кожної ітерації ми також просуваємо курсор за i
допомогою \1
. Нам просто потрібно переконатися, що не записувати це в групу 4
, тому що це зіпсується із збігом послідовних непарних чисел. Ось як ми доходимо до:
(\1(.(?(4).\1)))*