Regex (ECMAScript), 131 byte
Almeno -12 byte grazie a Deadcode (in chat)
(?=((xx+)(?=\2+$)|x+)+)(?=((x*?)(?=\1*$)(?=(\4xx+?)(\5*(?!(xx+)\7+$)\5)?$)(?=((x*)(?=\5\9*$)x)(\8*)$)x*(?=(?=\5$)\1|\5\10)x)+)\10|x
Provalo online!
L'output è la lunghezza della corrispondenza.
Le regex di ECMAScript rendono estremamente difficile contare qualsiasi cosa. Qualsiasi backref definito all'esterno di un loop sarà costante durante il loop, qualsiasi backref definito all'interno di un loop verrà ripristinato durante il looping. Pertanto, l'unico modo per portare lo stato attraverso iterazioni di loop è utilizzare la posizione di corrispondenza corrente. Questo è un singolo numero intero e può solo diminuire (beh, la posizione aumenta, ma la lunghezza della coda diminuisce, ed è ciò a cui possiamo fare matematica).
Date queste restrizioni, il semplice conteggio di interi coprimi sembra impossibile. Invece, usiamo la formula di Eulero per calcolare il totale.
Ecco come appare nello pseudocodice:
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
Ci sono due cose dubbie su questo.
Innanzitutto, non salviamo l'input, ma solo il prodotto corrente, quindi come possiamo arrivare ai fattori primi dell'input? Il trucco è che (N - (N / P)) ha gli stessi fattori primi> P di N. Potrebbe ottenere nuovi fattori primi <P, ma li ignoriamo comunque. Nota che questo funziona solo perché ripetiamo i fattori primi dal più piccolo al più grande, andando nell'altra direzione fallirebbe.
In secondo luogo, dobbiamo ricordare due numeri tra le iterazioni di loop (P e N, Z non contano poiché è costante) e ho appena detto che era impossibile! Per fortuna, possiamo far muovere quei due numeri in uno solo. Nota che, all'inizio del ciclo, N sarà sempre un multiplo di Z, mentre P sarà sempre inferiore a Z. Pertanto, possiamo solo ricordare N + P ed estrarre P con un modulo.
Ecco lo pseudo-codice leggermente più dettagliato:
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
Ed ecco la regex commentata:
# \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 come bonus ...
Regex (ECMAScript 2018, numero di corrispondenze), 23 byte
x(?<!^\1*(?=\1*$)(x+x))
Provalo online!
L'output è il numero di corrispondenze. ECMAScript 2018 introduce look-behind a lunghezza variabile (valutato da destra a sinistra), che consente di contare semplicemente tutti i numeri coprimi con l'input.
Si scopre che questo è indipendentemente lo stesso metodo usato dalla soluzione Retina di Leaky Nun , e il regex ha persino la stessa lunghezza ( e intercambiabile ). Lo lascio qui perché potrebbe essere interessante che questo metodo funzioni in ECMAScript 2018 (e non solo in .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