Nimrod: ~ 38.667 (580.000.000 / 15.000)
Jawaban ini menggunakan pendekatan yang cukup sederhana. Kode ini menggunakan ayakan bilangan prima sederhana yang menyimpan bilangan prima dari bilangan prima terkecil di setiap slot untuk bilangan komposit (nol untuk bilangan prima), kemudian menggunakan pemrograman dinamis untuk membangun fungsi totient pada rentang yang sama, kemudian menjumlahkan hasilnya. Program menghabiskan hampir seluruh waktunya untuk membuat ayakan, kemudian menghitung fungsi total dalam waktu yang singkat. Sepertinya itu turun untuk membangun saringan yang efisien (dengan sedikit twist yang satu juga harus dapat mengekstrak faktor utama untuk nomor komposit dari hasil dan harus menjaga penggunaan memori pada tingkat yang masuk akal).
Pembaruan: Peningkatan kinerja dengan mengurangi jejak memori dan meningkatkan perilaku cache. Dimungkinkan untuk memeras kinerja 5% -10% lebih banyak, tetapi peningkatan kompleksitas kode tidak sepadan. Pada akhirnya, algoritma ini terutama melatih bottleneck von Neumann dari CPU, dan ada sangat sedikit penyesuaian algoritme yang dapat mengatasi itu.
Juga memperbarui pembagi kinerja karena kode C ++ tidak dimaksudkan untuk dikompilasi dengan semua optimasi dan tidak ada orang lain yang melakukannya. :)
Pembaruan 2: Operasi ayakan yang dioptimalkan untuk peningkatan akses memori. Sekarang menangani bilangan prima kecil dalam jumlah besar melalui memcpy () (~ 5% speedup) dan melompati kelipatan 2, 3, dan 5 saat menyaring bilangan prima yang lebih besar (~ 10% speedup).
Kode C ++: 9,9 detik (dengan g ++ 4,9)
Kode Nimrod: 9,9 detik (dengan -d: rilis, 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)