Regex (ECMAScript), 131 octets
Au moins -12 octets grâce à Deadcode (dans le chat)
(?=((xx+)(?=\2+$)|x+)+)(?=((x*?)(?=\1*$)(?=(\4xx+?)(\5*(?!(xx+)\7+$)\5)?$)(?=((x*)(?=\5\9*$)x)(\8*)$)x*(?=(?=\5$)\1|\5\10)x)+)\10|x
Essayez-le en ligne!
La sortie est la longueur de la correspondance.
Les expressions régulières ECMAScript rendent extrêmement difficile de compter quoi que ce soit. Toute backref définie à l'extérieur d'une boucle sera constante pendant la boucle, toute backref définie à l'intérieur d'une boucle sera réinitialisée lors de la boucle. Ainsi, la seule façon de transporter l'état sur des itérations de boucle est d'utiliser la position de correspondance actuelle. C'est un entier unique, et il ne peut que diminuer (enfin, la position augmente, mais la longueur de la queue diminue, et c'est à cela que nous pouvons faire des calculs).
Compte tenu de ces restrictions, le simple comptage des nombres premiers semble impossible. Au lieu de cela, nous utilisons la formule d' Euler pour calculer le total.
Voici à quoi cela ressemble en pseudocode:
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
Il y a deux choses douteuses à ce sujet.
Tout d'abord, nous ne sauvegardons pas l'entrée, uniquement le produit actuel, alors comment pouvons-nous accéder aux facteurs premiers de l'entrée? L'astuce est que (N - (N / P)) a les mêmes facteurs premiers> P que N. Il peut gagner de nouveaux facteurs premiers <P, mais nous les ignorons quand même. Notez que cela ne fonctionne que parce que nous itérons sur les facteurs premiers du plus petit au plus grand, aller dans l'autre sens échouerait.
Deuxièmement, nous devons nous souvenir de deux nombres sur les itérations de boucle (P et N, Z ne compte pas car il est constant), et je viens de dire que c'était impossible! Heureusement, nous pouvons regrouper ces deux chiffres en un seul. Notez qu'au début de la boucle, N sera toujours un multiple de Z, tandis que P sera toujours inférieur à Z. Ainsi, nous pouvons simplement nous souvenir de N + P et extraire P avec un modulo.
Voici le pseudo-code légèrement plus détaillé:
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
Et voici l'expression régulière commentée:
# \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
Et en bonus…
Regex (ECMAScript 2018, nombre de correspondances), 23 octets
x(?<!^\1*(?=\1*$)(x+x))
Essayez-le en ligne!
La sortie est le nombre de correspondances. ECMAScript 2018 introduit une analyse de longueur variable (évaluée de droite à gauche), qui permet de compter simplement tous les nombres en coprime avec l'entrée.
Il s'avère que c'est indépendamment la même méthode utilisée par la solution de rétine de Leaky Nun , et que l'expression régulière est même de la même longueur ( et interchangeable ). Je le laisse ici car il peut être intéressant que cette méthode fonctionne dans ECMAScript 2018 (et pas seulement .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