นิมโรด: ~ 38,667 (580,000,000 / 15,000)
คำตอบนี้ใช้วิธีการที่ค่อนข้างง่าย รหัสใช้ตะแกรงหมายเลขเฉพาะอย่างง่ายซึ่งจัดเก็บพลังงานหลักที่เล็กที่สุดในแต่ละช่องสำหรับหมายเลขคอมโพสิต (ศูนย์สำหรับช่วงเวลา) จากนั้นใช้การเขียนโปรแกรมแบบไดนามิกเพื่อสร้างฟังก์ชั่น Totient ในช่วงเดียวกันจากนั้นจึงรวบรวมผลลัพธ์ โปรแกรมใช้เวลาเกือบทั้งหมดในการสร้างตะแกรงจากนั้นคำนวณฟังก์ชัน totient ในเสี้ยวเวลา ดูเหมือนว่าจะลงมาเพื่อสร้างตะแกรงที่มีประสิทธิภาพ (ด้วยการบิดเล็กน้อยที่หนึ่งยังต้องสามารถแยกปัจจัยสำคัญสำหรับตัวเลขคอมโพสิตจากผลและต้องเก็บการใช้หน่วยความจำในระดับที่เหมาะสม)
อัปเดต: ปรับปรุงประสิทธิภาพโดยลดขนาดหน่วยความจำและปรับปรุงพฤติกรรมแคช มีความเป็นไปได้ที่จะบีบเอาประสิทธิภาพที่เพิ่มขึ้น 5% -10% ออกไป แต่การเพิ่มความซับซ้อนของรหัสไม่คุ้มค่า ในที่สุดอัลกอริทึมนี้จะออกกำลังกายคอขวดของ von Neumann เป็นหลักและมีการปรับแต่งอัลกอริทึมน้อยมากที่สามารถแก้ไขได้
ปรับปรุงตัวหารประสิทธิภาพด้วยเนื่องจากรหัส C ++ นั้นไม่ได้หมายถึงการรวบรวมด้วยการปรับให้เหมาะสมทั้งหมดและไม่มีใครทำเช่นนั้น :)
อัพเดต 2: การดำเนินการบนตะแกรงที่ปรับให้เหมาะสมเพื่อการเข้าถึงหน่วยความจำที่ดี ตอนนี้จัดการช่วงเวลาขนาดเล็กจำนวนมากผ่าน memcpy () (~ 5% เร่งความเร็ว) และข้ามทวีคูณของ 2, 3 และ 5 เมื่อคัดกรองช่วงเวลาที่ใหญ่กว่า (~ 10% เร่งความเร็ว)
รหัส C ++: 9.9 วินาที (ด้วย g ++ 4.9)
รหัส Nimrod: 9.9 วินาที (ด้วย -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)