Nimrod: ~ 38 667 (580 000 000/15 000)
Cette réponse utilise une approche assez simple. Le code utilise un tamis à nombre premier simple qui stocke le nombre premier de la plus petite puissance principale dans chaque emplacement pour les nombres composites (zéro pour les nombres premiers), puis utilise la programmation dynamique pour construire la fonction totient sur la même plage, puis résume les résultats. Le programme passe pratiquement tout son temps à construire le tamis, puis calcule la fonction totient en une fraction du temps. Il semble que cela revient à construire un tamis efficace (avec la légère torsion que l'on doit également être en mesure d'extraire un facteur premier pour les nombres composites du résultat et doit maintenir l'utilisation de la mémoire à un niveau raisonnable).
Mise à jour: performances améliorées en réduisant l'empreinte mémoire et en améliorant le comportement du cache. Il est possible d'extraire 5 à 10% de performances supplémentaires, mais l'augmentation de la complexité du code n'en vaut pas la peine. En fin de compte, cet algorithme exerce principalement un goulot d'étranglement von Neumann d'un processeur, et il y a très peu de réglages algorithmiques qui peuvent contourner cela.
A également mis à jour le diviseur de performances car le code C ++ n'était pas censé être compilé avec toutes les optimisations et personne d'autre ne l'a fait. :)
Mise à jour 2: opération de tamisage optimisée pour un meilleur accès à la mémoire. Maintenant, gérer les petits nombres premiers en masse via memcpy () (~ 5% d'accélération) et ignorer les multiples de 2, 3 et 5 lors du tamisage des nombres premiers plus grands (~ 10% d'accélération).
Code C ++: 9,9 secondes (avec g ++ 4.9)
Code Nimrod: 9,9 secondes (avec -d: release, backend gcc 4.9)
proc handleSmallPrimes(sieve: var openarray[int32], m: int) =
# Small primes are handled as a special case through what is ideally
# the system's highly optimized memcpy() routine.
let k = 2*3*5*7*11*13*17
var sp = newSeq[int32](k div 2)
for i in [3,5,7,11,13,17]:
for j in countup(i, k, 2*i):
sp[j div 2] = int32(i)
for i in countup(0, sieve.high, len(sp)):
if i + len(sp) <= len(sieve):
copyMem(addr(sieve[i]), addr(sp[0]), sizeof(int32)*len(sp))
else:
copyMem(addr(sieve[i]), addr(sp[0]), sizeof(int32)*(len(sieve)-i))
# Fixing up the numbers for values that are actually prime.
for i in [3,5,7,11,13,17]:
sieve[i div 2] = 0
proc constructSieve(m: int): seq[int32] =
result = newSeq[int32](m div 2 + 1)
handleSmallPrimes(result, m)
var i = 19
# Having handled small primes, we only consider candidates for
# composite numbers that are relatively prime with 31. This cuts
# their number almost in half.
let steps = [ 1, 7, 11, 13, 17, 19, 23, 29, 31 ]
var isteps: array[8, int]
while i * i <= m:
if result[i div 2] == 0:
for j in 0..7: isteps[j] = i*(steps[j+1]-steps[j])
var k = 1 # second entry in "steps mod 30" list.
var j = 7*i
while j <= m:
result[j div 2] = int32(i)
j += isteps[k]
k = (k + 1) and 7 # "mod 30" list has eight elements.
i += 2
proc calculateAndSumTotients(sieve: var openarray[int32], n: int): int =
result = 1
for i in 2'i32..int32(n):
var tot: int32
if (i and 1) == 0:
var m = i div 2
var pp: int32 = 2
while (m and 1) == 0:
pp *= 2
m = m div 2
if m == 1:
tot = pp div 2
else:
tot = (pp div 2) * sieve[m div 2]
elif sieve[i div 2] == 0: # prime?
tot = i - 1
sieve[i div 2] = tot
else:
# find and extract the first prime power pp.
# It's relatively prime with i/pp.
var p = sieve[i div 2]
var m = i div p
var pp = p
while m mod p == 0 and m != p:
pp *= p
m = m div p
if m == p: # is i a prime power?
tot = pp*(p-1)
else:
tot = sieve[pp div 2] * sieve[m div 2]
sieve[i div 2] = tot
result += tot
proc main(n: int) =
var sieve = constructSieve(n)
let totSum = calculateAndSumTotients(sieve, n)
echo totSum
main(580_000_000)