Nimrod: ~ 38.667 (580.000.000 / 15.000)
Bu cevap oldukça basit bir yaklaşım kullanıyor. Kod, bileşik sayılar için her bir yuvadaki en küçük prime gücünün primerini depolayan basit bir prime numara elek kullanır (daha sonra primerler için sıfır), daha sonra, aynı fonksiyon üzerinde totient fonksiyonunu oluşturmak için dinamik programlama kullanır, ardından sonuçları toplar. Program neredeyse tüm zamanını elek için harcıyor, sonra da totient fonksiyonunu zamanın bir kısmında hesaplıyor. Etkili bir elek oluşturmak için aşağı geliyor gibi görünüyor (hafif bir bükümle sonuçta bileşik sayılar için bir ana faktör çıkarabilmek ve bellek kullanımını makul bir seviyede tutmak zorunda).
Güncelleme: Bellek ayak izini azaltarak ve önbellek davranışını iyileştirerek geliştirilmiş performans. % 5 -% 10 daha fazla performansı sıkıştırmak mümkündür, ancak kod karmaşıklığındaki artış buna değmez. Sonuçta, bu algoritma öncelikle bir işlemcinin von Neumann darboğazını uygular ve bunun üstesinden gelebilecek çok az algoritmik tweaks vardır.
Ayrıca C ++ kodunun tüm optimizasyonlarla derlenmesi gerekmediğinden ve başkası yapmadığı için performans bölenini güncelledi. :)
Güncelleme 2: Gelişmiş bellek erişimi için optimize edilmiş elek işlemi. Şimdi küçük primerleri memcpy () (~% 5 hızlanma) ile toplu halde ele alma ve büyük primerleri eleme (2% 10 hızlanma) olduğunda 2, 3 ve 5 katlarını atlama.
C ++ kodu: 9.9 saniye (g ++ 4.9 ile)
Nimrod kodu: 9.9 saniye (-d: bırakma, gcc 4.9 arka uç)
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)