Nimrod: ~ 38.667 (580.000.000 / 15.000)
Câu trả lời này sử dụng một cách tiếp cận khá đơn giản. Mã sử dụng một rây số nguyên tố đơn giản lưu trữ số nguyên tố có công suất nguyên tố nhỏ nhất trong mỗi khe cho các số tổng hợp (không cho số nguyên tố), sau đó sử dụng lập trình động để xây dựng hàm tổng trên cùng một phạm vi, sau đó tính tổng kết quả. Chương trình dành hầu như toàn bộ thời gian để xây dựng sàng, sau đó tính toán hàm tổng trong một phần nhỏ thời gian. Có vẻ như nó đi xuống để xây dựng một sàng hiệu quả (với một sự thay đổi nhỏ mà người ta cũng phải có thể trích xuất một yếu tố chính cho các số tổng hợp từ kết quả và phải giữ mức sử dụng bộ nhớ ở mức hợp lý).
Cập nhật: Cải thiện hiệu suất bằng cách giảm dung lượng bộ nhớ và cải thiện hành vi bộ đệm. Có thể giảm hiệu năng hơn 5% -10%, nhưng sự gia tăng độ phức tạp của mã không đáng. Cuối cùng, thuật toán này chủ yếu thực hiện nút cổ chai von Neumann của CPU và có rất ít điều chỉnh thuật toán có thể khắc phục điều đó.
Đồng thời cập nhật ước số hiệu năng do mã C ++ không có nghĩa là được biên dịch với tất cả các tối ưu hóa trên và không ai khác làm điều đó. :)
Cập nhật 2: Tối ưu hóa hoạt động sàng để truy cập bộ nhớ được cải thiện. Bây giờ xử lý hàng loạt số nguyên tố nhỏ thông qua memcpy () (~ 5% tăng tốc) và bỏ qua bội số của 2, 3 và 5 khi sàng các số nguyên tố lớn hơn (~ 10% tăng tốc).
Mã C ++: 9,9 giây (với g ++ 4.9)
Mã Nimrod: 9,9 giây (với -d: phát hành, gcc 4.9 phụ trợ)
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)