Đây thực sự không phải là khoa học máy tính ...
Bạn tạo một bảng d nơi bạn lưu tổng các ước của k, với k = 1 đến M, trong đó M = 5⋅106. Đó là phần quan trọng về thời gian. Sau đó, bạn tạo một bảng trong đó bạn lưu tổng các ước cho tất cả 1 ≤ j ≤ k, với k = 1 đến M. Điều đó thật dễ dàng,s0=0, sk+1=sk+dk+1. Và sau đó f (L, R) =sR−sL−1.
Bảng đầu tiên là vấn đề. Bạn xử lý việc này trongO(nlogn). Và bạn chỉ cần một yếu tố hai, bạn nói ...
Bạn sẽ có một mảng d với 5 triệu mục, có thể là 4 byte cho mỗi mục = 20 Megabyte. Trên một bộ xử lý điển hình mà bạn sẽ có trong máy tính ở nhà, 20 Megabyte không phù hợp với bất kỳ bộ đệm nào. Và mã của bạn thực hiện rất nhiều quyền truy cập vào các phần tử của mảng đó theo thứ tự ngẫu nhiên. Đối với mỗi ước số k tiềm năng, bạn truy cập tất cả các số chia hết cho k và tăng tổng số chia cho k.
Chúng ta hãy làm điều đó với số lượt truy cập ít hơn: Khi bạn truy cập j chia hết cho k, hãy thêm hai ước số k và j / k. Nhưng khi bạn làm điều đó, hãy bắt đầu vớij=k2, chỉ thêm k (vì k = j / k và bạn không muốn đếm số chia hai lần), sau đó thêm k và j / k để biết thêm j. Bạn không cần chia, vì j / k sẽ bằng k + 1, k + 2, k + 3, v.v. Chúng tôi khởi tạo mảng cho trường hợp k = 1, đó là đặt A [j] = 1 + j / 1 cho j 2.
A [1] = 1
for (j = 2; j ≤ M; j += 1)
A [j] = 1 + j
for (k = 2; k*k ≤ M; k += 1)
j = k*k
A [j] += k
j += k
s = k + (k + 1)
while j ≤ M
A [j] += s
j += k
s += 1 // s equals k + j / k
Bạn không lưu hoạt động. Tuy nhiên, hiện bạn đang truy cập vào mảng A theo mô hình thường xuyên hơn nhiều, vì vậy điều này sẽ giúp bạn tiết kiệm thời gian vì việc truy cập vào các mục sẽ nhanh hơn. j sẽ nhỏ hơn, làm cho số lần lặp cho mỗi j lớn hơn, điều này sẽ làm cho dự đoán nhánh hoạt động tốt hơn.
Để cải thiện thêm, bạn sẽ tìm hiểu có bao nhiêu mục mảng phù hợp với bộ đệm của bộ xử lý trong máy tính của bạn, sau đó thực hiện toàn bộ mã cho các phần phụ của mảng (ví dụ: chỉ thay đổi A [0] thành A [99999], sau đó thay đổi A [100000] đến A [199999], v.v.). Theo cách đó, hầu hết các truy cập bộ nhớ sẽ chỉ truy cập bộ nhớ cache, có thể nhanh hơn đáng kể.
Bạn đang thực hiện tra cứu N trong một bảng có kích thước M. Nếu M lớn hơn N, thì có lẽ bạn nên nghĩ về các phương pháp không xây dựng bảng này và có thể chậm hơn rất nhiều cho mỗi lần tra cứu, nhưng tổng thể nhanh hơn do số lượng nhỏ tra cứu. Ngay cả trong trường hợp ở đây có N ≤ 100.000 và M = 5.000.000, bạn có thể không tính các ước số 1, 2, 3, 4, j / 1, j / 2, j / 3, j / 4 trong bảng (điều này làm cho nó nhanh hơn một chút để xây dựng) và xử lý nó trong quá trình tra cứu.
Hoặc bạn có thể thêm tổng các ước số cho các số lẻ, sau đó tính tổng các ước cho các số chẵn (nếu tổng các ước của một số lẻ k là s, thì tổng của 2k là 3 giây, với 4k là 7 giây , với 8k, nó là 15 giây, v.v.), sẽ tiết kiệm gần như một yếu tố 2.
Tái bút Tôi đã đo nó ... làm cho thuật toán đếm tất cả các ước số trở nên thân thiện hơn bằng cách thêm cả j và k / j nhân đôi tốc độ. Tính tổng các ước số cho k lẻ trước, sau đó tính k chẵn từ các giá trị lẻ, làm cho nó nhanh hơn tổng cộng 7 lần. Rõ ràng tất cả chỉ là yếu tố không đổi.