Regex (ECMAScript), 131 bytes
Pelo menos -12 bytes graças ao código morto (no chat)
(?=((xx+)(?=\2+$)|x+)+)(?=((x*?)(?=\1*$)(?=(\4xx+?)(\5*(?!(xx+)\7+$)\5)?$)(?=((x*)(?=\5\9*$)x)(\8*)$)x*(?=(?=\5$)\1|\5\10)x)+)\10|x
Experimente online!
Saída é o comprimento da partida.
As expressões regulares ECMAScript tornam extremamente difícil contar qualquer coisa. Qualquer backref definido fora de um loop será constante durante o loop, qualquer backref definido dentro de um loop será redefinido durante o loop. Portanto, a única maneira de transportar o estado pelas iterações do loop é usando a posição de correspondência atual. Esse é um número inteiro único e só pode diminuir (bem, a posição aumenta, mas o comprimento da cauda diminui, e é para isso que podemos fazer contas).
Dadas essas restrições, simplesmente contar números de coprime parece impossível. Em vez disso, usamos a fórmula de Euler para calcular o totiente.
Veja como fica no pseudocódigo:
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
Há duas coisas duvidosas sobre isso.
Primeiro, não salvamos a entrada, apenas o produto atual; então, como podemos obter os principais fatores da entrada? O truque é que (N - (N / P)) tem os mesmos fatores primos> P que N. Ele pode ganhar novos fatores primos <P, mas nós os ignoramos de qualquer maneira. Observe que isso só funciona porque iteramos os fatores primos do menor para o maior, seguindo o caminho inverso.
Segundo, precisamos lembrar dois números nas iterações de loop (P e N, Z não conta, pois é constante), e eu apenas disse que era impossível! Felizmente, podemos misturar esses dois números em um único. Observe que, no início do loop, N sempre será um múltiplo de Z, enquanto P sempre será menor que Z. Assim, podemos apenas lembrar de N + P e extrair P com um módulo.
Aqui está o pseudo-código um pouco mais detalhado:
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
E aqui está o regex comentado:
# \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
E como um bônus ...
Regex (ECMAScript 2018, número de correspondências), 23 bytes
x(?<!^\1*(?=\1*$)(x+x))
Experimente online!
Saída é o número de correspondências. O ECMAScript 2018 apresenta look-behind de comprimento variável (avaliado da direita para a esquerda), o que torna possível contar simplesmente todos os números de coprime com a entrada.
Acontece que esse é independentemente o mesmo método usado pela solução Retina da Leaky Nun , e o regex tem até o mesmo comprimento ( e é intercambiável ). Estou deixando aqui porque pode ser interessante que esse método funcione no ECMAScript 2018 (e não apenas no .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