Регулярное выражение (ECMAScript), 131 байт
По крайней мере -12 байт благодаря Deadcode (в чате)
(?=((xx+)(?=\2+$)|x+)+)(?=((x*?)(?=\1*$)(?=(\4xx+?)(\5*(?!(xx+)\7+$)\5)?$)(?=((x*)(?=\5\9*$)x)(\8*)$)x*(?=(?=\5$)\1|\5\10)x)+)\10|x
Попробуйте онлайн!
Выход - длина матча.
Регулярные выражения ECMAScript чрезвычайно усложняют подсчет. Любая обратная ссылка, определенная вне цикла, будет постоянной во время цикла, любая обратная ссылка, определенная внутри цикла, будет сброшена при цикле. Таким образом, единственный способ переноса состояния по итерациям цикла - использование текущей позиции совпадения. Это одно целое число, и оно может только уменьшаться (ну, позиция увеличивается, но длина хвоста уменьшается, и это то, что мы можем сделать по математике).
Учитывая эти ограничения, простой подсчет простых чисел кажется невозможным. Вместо этого мы используем формулу Эйлера для вычисления значения.
Вот как это выглядит в псевдокоде:
N = input
Z = largest prime factor of N
P = 0
do:
P = smallest number > P that’s a prime factor of N
N = N - (N / P)
while P != Z
return N
В этом есть две сомнительные вещи.
Во-первых, мы не сохраняем входные данные, а только текущий продукт, так как же мы можем получить основные факторы входных данных? Хитрость в том, что (N - (N / P)) имеет те же простые факторы> P, что и N. Он может получить новые простые факторы <P, но мы все равно их игнорируем. Обратите внимание, что это работает только потому, что мы перебираем простые множители от наименьшего к наибольшему, иное движение в обратном направлении завершится неудачей
Во-вторых, мы должны помнить два числа в итерациях цикла (P и N, Z не считается, поскольку он постоянен), и я только что сказал, что это невозможно! К счастью, мы можем объединить эти два числа в одном. Обратите внимание, что в начале цикла N всегда будет кратно Z, а P всегда будет меньше Z. Таким образом, мы можем просто запомнить N + P и извлечь P с помощью модуля.
Вот немного более подробный псевдокод:
N = input
Z = largest prime factor of N
do:
P = N % Z
N = N - P
P = smallest number > P that’s a prime factor of N
N = N - (N / P) + P
while P != Z
return N - Z
И вот прокомментированное регулярное выражение:
# \1 = largest prime factor of N
# Computed by repeatedly dividing N by its smallest factor
(?= ( (xx+) (?=\2+$) | x+ )+ )
(?=
# Main loop!
(
# \4 = N % \1, N -= \4
(x*?) (?=\1*$)
# \5 = next prime factor of N
(?= (\4xx+?) (\5* (?!(xx+)\7+$) \5)? $ )
# \8 = N / \5, \9 = \8 - 1, \10 = N - \8
(?= ((x*) (?=\5\9*$) x) (\8*) $ )
x*
(?=
# if \5 = \1, break.
(?=\5$) \1
|
# else, N = (\5 - 1) + (N - B)
\5\10
)
x
)+
) \10
И в качестве бонуса ...
Regex (ECMAScript 2018, количество совпадений), 23 байта
x(?<!^\1*(?=\1*$)(x+x))
Попробуйте онлайн!
Выход - это количество совпадений. В ECMAScript 2018 вводится функция обратной длины переменной (вычисляется справа налево), которая позволяет просто считать все числа, взаимно простые с вводом.
Оказывается, это независимо тот же метод, который используется в решении Retina Лики Нун , и регулярное выражение имеет даже ту же длину ( и взаимозаменяемо ). Я оставляю это здесь, потому что может быть интересно, что этот метод работает в ECMAScript 2018 (а не только .NET).
# Implicitly iterate from the input to 0
x # Don’t match 0
(?<! ) # Match iff there is no...
(x+x) # integer >= 2...
(?=\1*$) # that divides the current number...
^\1* # and also divides the input