Nimrod: ~ 38.667 (580.000.000 / 15.000)
Diese Antwort verwendet einen ziemlich einfachen Ansatz. Der Code verwendet ein einfaches Primzahlsieb, das die Primzahl der kleinsten Primzahlleistung in jedem Schlitz für zusammengesetzte Zahlen (Null für Primzahlen) speichert, dann die dynamische Programmierung verwendet, um die Totientenfunktion über denselben Bereich zu konstruieren, und dann die Ergebnisse summiert. Das Programm verbringt praktisch die ganze Zeit damit, das Sieb aufzubauen, und berechnet dann die Totientenfunktion in einem Bruchteil der Zeit. Es sieht so aus, als käme es darauf an, ein effizientes Sieb zu konstruieren (mit der leichten Wendung, dass man auch einen Primfaktor für zusammengesetzte Zahlen aus dem Ergebnis extrahieren und die Speichernutzung auf einem vernünftigen Niveau halten muss).
Update: Verbesserte Leistung durch Reduzierung des Speicherbedarfs und Verbesserung des Cache-Verhaltens. Es ist möglich, 5% -10% mehr Leistung zu erzielen, aber die Steigerung der Codekomplexität ist es nicht wert. Letztendlich übt dieser Algorithmus hauptsächlich den von Neumann-Engpass einer CPU aus, und es gibt nur sehr wenige algorithmische Optimierungen, die das umgehen können.
Außerdem wurde der Performance-Teiler aktualisiert, da der C ++ - Code nicht mit allen Optimierungen kompiliert werden sollte und niemand anderes dies tat. :)
Update 2: Optimierter Siebbetrieb für verbesserten Speicherzugriff. Behandeln Sie jetzt kleine Primzahlen in großen Mengen mit memcpy () (~ 5% Beschleunigung) und überspringen Sie beim Sieben größerer Primzahlen ein Vielfaches von 2, 3 und 5 (~ 10% Beschleunigung).
C ++ Code: 9,9 Sekunden (mit g ++ 4.9)
Nimrod Code: 9.9 Sekunden (mit -d: release, gcc 4.9 backend)
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)