Wiki › Thuật toán › Toán học › Đếm số nguyên tố đến N lớn (Lucy/Meissel)

Đếm số nguyên tố đến N lớn (Lucy/Meissel)

huunguyen huunguyen Updated Tháng 9. 29, 2026 Đọc 21 phút 9 lượt xem

Bài toán: cho N rất lớn (tới 1011, 1012), tính π(N) là số số nguyên tố không vượt quá N. Sàng Eratosthenes cần O(N) bộ nhớ nên không dùng được khi N>109 (một mảng bool cỡ 1011 là 100 GB). Thuật toán Lucy_Hedgehog (đặt theo tên người đăng nó trên diễn đàn Project Euler) tính π(N) trong O(N3/4) thời gian và chỉ O(N) bộ nhớ: với N=1011 chạy dưới 0,1 giây, với N=1012 khoảng 0,4 giây. Cùng khung đó tính được tổng các số nguyên tố, đếm số nguyên tố theo lớp đồng dư, và đếm số lượng số có ước nguyên tố nhỏ nhất bằng p cho trước.

Ý tưởng / Trực giác

Nhận xét 1: chỉ có O(N) giá trị ⌊N/i⌋ khác nhau

Xét tập V={⌊N/i⌋:1≤i≤N}. Với i≤N có không quá N giá trị; với i>N thì ⌊N/i⌋<N, lại không quá N giá trị. Vậy |V|≤2N.

Tập V có một tính chất "đóng" rất quý: nếu v∈V thì ⌊v/p⌋∈V với mọi p, vì

⌊⌊N/i⌋p⌋=⌊Nip⌋.

Nghĩa là nếu ta tính một đại lượng S(v) cho mọi v∈V, mà công thức truy hồi của S(v) chỉ cần S(⌊v/p⌋) và S tại giá trị nhỏ, thì mọi thứ cần dùng đều nằm trong V. Ta sẽ tính π(v) cho cả 2N giá trị này cùng lúc, và đáp án là π(N) (ứng với i=1).

Nhận xét 2: sàng Eratosthenes "theo số lượng"

Nhớ lại sàng Eratosthenes: duyệt số nguyên tố p tăng dần, gạch bỏ các bội p2,p2+p,…. Sau khi xử lý xong mọi p≤N, số còn lại trong [2,N] đúng là các số nguyên tố.

Lucy không gạch từng số, mà chỉ theo dõi số lượng còn lại. Định nghĩa

S(v,p)=#{2≤x≤v:x nguyên tố, hoặc ước nguyên tố nhỏ nhất của x lớn hơn p}.

Tức là S(v,p) là số phần tử còn sống trong đoạn [2,v] sau khi sàng bằng mọi số nguyên tố ≤p. Ta có:

  • Khởi tạo S(v,1)=v−1 (chưa gạch gì, còn đủ 2,3,…,v).
  • Khi p2>v: sàng thêm p không gạch gì trong [2,v], nên S(v,p)=S(v,p−1). Đặc biệt S(v,⌊v⌋)=π(v).
  • Nếu p không phải số nguyên tố: không có gì thay đổi, S(v,p)=S(v,p−1).
Công thức truy hồi và vì sao đúng

Với p nguyên tố và p2≤v:

S(v,p)=S(v,p−1)−[S(⌊v/p⌋,p−1)−S(p−1,p−1)]

Lượng bị trừ phải bằng số phần tử bị gạch ở bước p, tức số hợp số x≤v có ước nguyên tố nhỏ nhất đúng bằng p. Mỗi số như vậy viết duy nhất được thành x=p·m với:

  • p≤m≤⌊v/p⌋;
  • m không có ước nguyên tố nào nhỏ hơn p (nếu không thì ước nhỏ nhất của x nhỏ hơn p).

Đếm các m như vậy:

  • S(⌊v/p⌋,p−1) đếm các m∈[2,⌊v/p⌋] là số nguyên tố hoặc không có ước nguyên tố <p. Tập này gồm hai phần: các số nguyên tố nhỏ hơn p (bị đếm vì là số nguyên tố, nhưng không hợp lệ) và mọi m≥p không có ước nguyên tố <p (hợp lệ).
  • Số nguyên tố nhỏ hơn p có đúng π(p−1) số, và π(p−1)=S(p−1,p−1): mọi số nguyên tố ≤p−1 đều ≤p−1, nên sau khi sàng bằng mọi số nguyên tố ≤p−1 thì đoạn [2,p−1] đã được sàng xong, chỉ còn các số nguyên tố.

Trừ đi là ra đúng số m hợp lệ, nên công thức đúng. Chú ý ⌊v/p⌋≥p>p−1 nên hiệu luôn không âm.

Cài đặt thực tế chỉ giữ một mảng S[v] cho v∈V và cập nhật tại chỗ theo từng p. Để S[⌊v/p⌋] vẫn là giá trị của bước trước (p−1) khi dùng, ta duyệt v giảm dần: ⌊v/p⌋<v nên nó chưa bị sửa ở bước p. Còn S[p−1] thì không bị sửa ở bước p vì p−1<p2.

Nhận biết p nguyên tố cũng không cần sàng riêng: p nguyên tố khi và chỉ khi S[p]≠S[p−1] (lúc xét tới p, hai giá trị này đã là π(p) và π(p−1)).

Ví dụ chạy tay

N=30, ⌊30⌋=5. Tập V gồm các giá trị ⌊30/i⌋ với i=1..5 (phần "lớn") và 1..5 (phần "nhỏ", các v≤⌊30⌋=5):

V (giảm dần):  30  15  10   7   6 |  5   4   3   2   1
               --- large (30/i) -- | ------ small ------

Khởi tạo S[v]=v−1:

v      : 30  15  10   7   6   5   4   3   2   1
S[v]   : 29  14   9   6   5   4   3   2   1   0

Bước p=2 (S[2]=1≠S[1]=0 nên 2 nguyên tố; below =S[1]=0). Cập nhật mọi v≥4, giảm dần:

v=30: S[30] -= S[15] - 0 = 14   -> 15    (gạch 4,6,...,30: 14 số chẵn)
v=15: S[15] -= S[7]  - 0 = 6    -> 8
v=10: S[10] -= S[5]  - 0 = 4    -> 5
v=7 : S[7]  -= S[3]  - 0 = 2    -> 4
v=6 : S[6]  -= S[3]  - 0 = 2    -> 3
v=5 : S[5]  -= S[2]  - 0 = 1    -> 3
v=4 : S[4]  -= S[2]  - 0 = 1    -> 2
         ^ S[15], S[7], ... vẫn là giá trị cũ vì duyệt v giảm dần

v      : 30  15  10   7   6   5   4   3   2   1
S[v]   : 15   8   5   4   3   3   2   2   1   0

Bước p=3 (S[3]=2≠S[2]=1; below =S[2]=1). Cập nhật v≥9:

v=30: S[30] -= S[10] - 1 = 4    -> 11    (gạch 9, 15, 21, 27)
v=15: S[15] -= S[5]  - 1 = 2    -> 6     (gạch 9, 15)
v=10: S[10] -= S[3]  - 1 = 1    -> 4     (gạch 9)

v      : 30  15  10   7   6   5   4   3   2   1
S[v]   : 11   6   4   4   3   3   2   2   1   0

Bước p=4: S[4]=2=S[3] nên 4 là hợp số, bỏ qua.

Bước p=5 (S[5]=3≠S[4]=2; below =S[4]=2). Chỉ v=30≥25:

v=30: S[30] -= S[6] - 2 = 1     -> 10    (gạch 25; còn 35 > 30)

v      : 30  15  10   7   6   5   4   3   2   1
S[v]   : 10   6   4   4   3   3   2   2   1   0
         ^^

Kết quả π(30)=10 (các số 2,3,5,7,11,13,17,19,23,29). Tiện thể ta có luôn π(15)=6, π(10)=4, π(7)=4: cả bảng là π(v) cho mọi v∈V.

Cài đặt

Lưu S bằng hai mảng small / large

Không dùng map (chậm O(log) mỗi lần truy cập). Với r=⌊N⌋:

  • small[v] lưu S(v) với v≤r;
  • large[i] lưu S(⌊N/i⌋) với i≤r.

Khi cần S(⌊v/p⌋) với v=⌊N/i⌋: vì ⌊v/p⌋=⌊N/(ip)⌋, nếu ip≤r thì đọc large[i*p], ngược lại ⌊N/(ip)⌋<N nên đọc small[N/(i*p)]. Mọi truy cập đều là đọc mảng trực tiếp theo chỉ số; phép chia chỉ còn ở N / (i*p) (khi ip>r), ở v / p trong vòng small, và một lần n / p2 cho mỗi số nguyên tố p.

Thứ tự "v giảm dần" tương ứng với: duyệt large[i] theo i tăng dần, rồi small[v] theo v giảm dần.

C++: đếm số nguyên tố
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

// pi(n) by Lucy_Hedgehog's method: O(n^{3/4}) time, O(sqrt n) memory.
ll countPrimes(ll n) {
    if (n < 2) return 0;
    ll r = sqrtl((long double)n);
    while (r * r > n) r--;                 // fix floating-point error
    while ((r + 1) * (r + 1) <= n) r++;
    // small[v] = S(v) for v <= r;  large[i] = S(n / i) for i <= r
    vector<ll> small(r + 1), large(r + 1);
    for (ll v = 1; v <= r; v++) small[v] = v - 1;
    for (ll i = 1; i <= r; i++) large[i] = n / i - 1;

    for (ll p = 2; p <= r; p++) {
        if (small[p] == small[p - 1]) continue;   // p is composite
        ll below = small[p - 1];                  // pi(p - 1)
        ll p2 = p * p;
        // large values: v = n / i >= p^2  <=>  i <= n / p^2
        ll iEnd = min(r, n / p2);
        for (ll i = 1; i <= iEnd; i++) {
            ll ip = i * p;                        // S(v / p) = S(n / (i * p))
            ll sub = (ip <= r) ? large[ip] : small[n / ip];
            large[i] -= sub - below;
        }
        // small values, decreasing, only v >= p^2
        for (ll v = r; v >= p2; v--)
            small[v] -= small[v / p] - below;
    }
    return large[1];                              // S(n / 1) = pi(n)
}

int main() {
    ll n;
    cin >> n;
    cout << countPrimes(n) << "\n";
}

Kiểm chứng: π(1010)=455052511, π(1011)=4118054813, π(1012)=37607912018.

C++: tổng các số nguyên tố (theo modulo)

Thay "đếm" bằng "cộng": khởi tạo G(v)=2+3+⋯+v và mỗi bước trừ p·[G(⌊v/p⌋)−G(p−1)]. Hệ số p xuất hiện vì mỗi số bị gạch có dạng x=p·m nên đóng góp p·m. Công thức chỉ đúng khi hàm được cộng (ở đây f(x)=x; tương tự với f(x)=xk hoặc f(x)=1) là nhân tính hoàn toàn: f(pm)=f(p)f(m) với mọi p,m.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;

// Sum of primes <= n, modulo MOD.
ll sumPrimes(ll n) {
    if (n < 2) return 0;
    ll r = sqrtl((long double)n);
    while (r * r > n) r--;
    while ((r + 1) * (r + 1) <= n) r++;
    auto prefix = [](ll v) {             // (2 + 3 + ... + v) mod MOD
        ll a = v, b = v + 1;
        if (a % 2 == 0) a /= 2; else b /= 2;
        return ((a % MOD) * (b % MOD) % MOD - 1 + MOD) % MOD;
    };
    vector<ll> small(r + 1), large(r + 1), cnt(r + 1);
    for (ll v = 1; v <= r; v++) small[v] = prefix(v), cnt[v] = v - 1;
    for (ll i = 1; i <= r; i++) large[i] = prefix(n / i);

    for (ll p = 2; p <= r; p++) {
        if (cnt[p] == cnt[p - 1]) continue;       // exact counts detect primes
        ll below = small[p - 1], p2 = p * p, pm = p % MOD;
        ll iEnd = min(r, n / p2);
        for (ll i = 1; i <= iEnd; i++) {
            ll ip = i * p;
            ll sub = (ip <= r) ? large[ip] : small[n / ip];
            large[i] = ((large[i] - pm * ((sub - below + MOD) % MOD)) % MOD + MOD) % MOD;
        }
        for (ll v = r; v >= p2; v--) {
            small[v] = ((small[v] - pm * ((small[v / p] - below + MOD) % MOD)) % MOD + MOD) % MOD;
            cnt[v] -= cnt[v / p] - cnt[p - 1];    // keep exact pi(v) for small v
        }
    }
    return large[1];
}

int main() {
    ll n;
    cin >> n;
    cout << sumPrimes(n) << "\n";
}

Mảng cnt giữ π(v) chính xác cho v≤r chỉ để nhận biết số nguyên tố: sau khi lấy modulo, hai giá trị tổng có thể trùng nhau một cách tình cờ nên không dùng small[p] == small[p-1] được nữa. Kiểm chứng: tổng các số nguyên tố ≤1011 là 201467077743744681014, chia 109+7 dư 475146683.

Python

Python đủ nhanh tới khoảng N=1010 (khoảng 1,5 giây). Dùng dict cho gọn; muốn nhanh hơn thì đổi sang hai list small/large như bản C++.

from math import isqrt

def count_primes(n):
    """pi(n) by Lucy_Hedgehog's method."""
    if n < 2:
        return 0
    r = isqrt(n)
    vals = [n // i for i in range(1, r + 1)]        # large values, decreasing
    vals += list(range(vals[-1] - 1, 0, -1))        # small values, decreasing
    S = {v: v - 1 for v in vals}
    for p in range(2, r + 1):
        if S[p] == S[p - 1]:
            continue                                # p is composite
        below = S[p - 1]
        p2 = p * p
        for v in vals:                              # decreasing order
            if v < p2:
                break
            S[v] -= S[v // p] - below
    return S[n]

print(count_primes(int(input())))

Độ phức tạp

Bộ nhớ: O(N), hai mảng độ dài ⌊N⌋+1. Với N=1012 chỉ khoảng 2·106 số long long, tức 16 MB.

Thời gian: Ở bước p, ta cập nhật mọi v∈V có v≥p2. Đổi thứ tự tính tổng: mỗi v được cập nhật một lần cho mỗi số nguyên tố p≤v, nên tổng công việc là

∑v∈Vπ(v)≤∑v∈Vv.

  • Phần lớn, v=N/i với i≤N: ∑i≤NN/i=N∑i≤Ni−1/2≈N·2N1/4=2N3/4.
  • Phần nhỏ, v≤N: ∑v≤Nv≈23N3/4.

Vậy thời gian O(N3/4). Chính xác hơn, vì chỉ số nguyên tố mới gây cập nhật và π(x)≈x/lnx, tổng thực tế là O(N3/4/logN). Đo thực tế số lần cập nhật: 3·106 với N=109, 8·107 với N=1011, 4·108 với N=1012; mỗi lần chỉ gồm vài lần đọc mảng, một phép trừ và nhiều nhất một phép chia 64 bit (n / ip ở vòng large khi ip>r, hoặc v / p ở vòng small), nên vẫn chạy dưới 0,5 giây.

Vòng for p duyệt tới N cả số hợp, nhưng mỗi số hợp chỉ tốn O(1) nên không ảnh hưởng.

N Sàng Eratosthenes Lucy
109 1 GB bool (khoảng 60 MB nếu dùng bitset chỉ số lẻ), vài giây <0,01 s, 0,5 MB
1011 không khả thi khoảng 0,07 s, 5 MB
1012 không khả thi khoảng 0,4 s, 16 MB

⚠️ Lỗi thường gặp

  • Tính ⌊N⌋ sai do số thực. double chỉ có 53 bit, nên (ll)sqrt(N) có thể lệch 1 khi N lớn và sát một số chính phương (vd (ll)sqrt(1e18 - 1) ra 109 thay vì 999999999). Triệu chứng: thiếu hoặc thừa một giá trị trong V, đọc small[] ngoài mảng. Cách tránh: sau sqrtl luôn chỉnh bằng hai vòng while, hoặc dùng math.isqrt trong Python.
  • Duyệt v tăng dần. Khi đó S[⌊v/p⌋] đã bị sửa ở bước p trước khi dùng, số bị gạch bị trừ thiếu. Triệu chứng: π(30) ra 14 thay vì 10. Cách tránh: large[i] theo i tăng (tức v giảm), rồi small[v] theo v giảm.
  • Vòng for p dừng ở p < r thay vì p <= r. Khi N=r2 (vd N=25), số p=r vẫn phải gạch p2=N. Triệu chứng: π(25) ra 10 thay vì 9. Cách tránh: điều kiện p <= r.
  • Tràn số khi tính i * p hoặc p * p. Với N=1012 thì p≤106, p2≤1012: vượt int. Cách tránh: mọi biến là long long. Với bản tính tổng, p·G phải lấy modulo trước khi nhân, và tổng các số nguyên tố ≤1011 đã vượt 264, nên hoặc lấy modulo hoặc dùng __int128.
  • Nhận biết số nguyên tố bằng small[p] != small[p-1] trong bản có modulo. Hai tổng theo modulo có thể trùng tình cờ. Cách tránh: giữ song song một mảng đếm chính xác, hoặc sàng Eratosthenes riêng tới N.
  • Áp dụng cho hàm không nhân tính hoàn toàn. Công thức "trừ f(p)·[…]" chỉ đúng nếu f(pm)=f(p)f(m) với mọi m, như f(x)=1, x, x2, hay ký tự χ4 trong phần mở rộng dưới. Với φ, μ hay "số ước" thì không dùng trực tiếp được (xem phần Mở rộng).
  • Dùng map/unordered_map trong C++. Chạy đúng nhưng chậm hơn 5 đến 20 lần, có thể TLE với N=1012. Cách tránh: hai mảng small/large.

Biến thể / Mở rộng

Đếm số theo ước nguyên tố nhỏ nhất

Lượng bị trừ ở bước p khi v=N, tức S(⌊N/p⌋,p−1)−π(p−1), chính là số hợp số ≤N có ước nguyên tố nhỏ nhất bằng p. Cộng thêm 1 (chính số p) ta được

#{2≤x≤N:spf(x)=p}=S(⌊N/p⌋,p−1)−π(p−1)+1,p≤N.

Với số nguyên tố p>N thì đáp án là đúng 1 (chỉ có x=p), vì mọi hợp số có ước nguyên tố nhỏ nhất p đều ≥p2>N: viết x=kp với k≥2, nếu k<p thì k có ước nguyên tố nhỏ hơn p (mâu thuẫn), còn nếu k≥p thì x≥p2>N. Ghi lại lượng trừ tại large[1] ở mỗi bước là có ngay bảng này, không tốn thêm độ phức tạp. Khi N nhỏ (cỡ 107), bảng tương tự lấy được trực tiếp từ mảng lp[] của sàng tuyến tính.

Đếm số nguyên tố theo lớp đồng dư mod 4

Hàm χ4(x)=0,1,0,−1 khi x≡0,1,2,3(mod4) là nhân tính hoàn toàn. Cách đơn giản hơn là giữ hai mảng: A(v) đếm số lẻ còn sống ≡1(mod4), B(v) đếm số ≡3(mod4), bỏ qua p=2 (không đưa số chẵn vào ngay từ đầu):

  • khởi tạo A(v)=⌊(v−1)/4⌋, B(v)=⌊(v+1)/4⌋;
  • với p≡1(mod4): x=pm cùng lớp với m, nên A trừ phần của A, B trừ phần của B;
  • với p≡3(mod4): x=pm đổi lớp so với m, nên A trừ phần của B và ngược lại.

Cùng ý tưởng mở rộng cho mọi modulo nhỏ k: giữ φ(k) mảng, mỗi số nguyên tố p hoán vị các lớp theo phép nhân với pmodk.

Hàm nhân tính tổng quát: Min_25 và sàng Du

Muốn tính ∑x≤Nf(x) cho f nhân tính (không hoàn toàn) như φ, μ, số ước: bước 1 dùng đúng Lucy để có ∑p≤vf(p) cho mọi v∈V (vì f(p) thường là đa thức theo p), bước 2 cộng thêm các hợp số theo ước nguyên tố nhỏ nhất. Đó là sàng Min_25, chạy khoảng O(N3/4/logN). Riêng tổng tiền tố của μ và φ còn có sàng Du (Du's sieve) dựa trên tích Dirichlet, O(N2/3). Cả hai đều dựa trên cùng nhận xét "chỉ có O(N) giá trị ⌊N/i⌋".

Giới thiệu Meissel-Lehmer

Legendre định nghĩa ϕ(x,a) là số số nguyên 1≤y≤x không chia hết cho a số nguyên tố đầu tiên p1,…,pa. Theo bao hàm, loại trừ, ϕ có truy hồi

ϕ(x,a)=ϕ(x,a−1)−ϕ(⌊x/pa⌋,a−1),ϕ(x,0)=x.

Liên hệ chính xác với Lucy: với pa≤v,

S(v,pa)=ϕ(v,a)+a−1,

vì ϕ(v,a) đếm số 1 cùng mọi x≥2 có ước nguyên tố nhỏ nhất lớn hơn pa, còn S(v,pa) bỏ số 1 nhưng giữ thêm a số nguyên tố p1,…,pa. Meissel chọn a=π(x3), b=π(x). Các số ≤x không có ước nguyên tố nào trong p1..pa gồm: số 1, các số nguyên tố pa+1,…,pπ(x), và các tích đúng hai số nguyên tố pjpk với a<j≤k (ba thừa số thì vượt x). Với mỗi thừa số nhỏ pj (cần pj2≤x, tức j≤b), thừa số lớn pk chạy qua các số nguyên tố trong [pj,x/pj], có π(⌊x/pj⌋)−(j−1) giá trị. Suy ra

π(x)=ϕ(x,a)+a−1−P2(x,a),P2(x,a)=∑j=a+1b(π(⌊x/pj⌋)−(j−1)).

P2 cần π tại các điểm ≤x2/3, lấy bằng một sàng đoạn; ϕ(x,a) tính bằng đệ quy có cắt tỉa và bảng tra cho a nhỏ. Lehmer, rồi Lagarias, Miller, Odlyzko cải tiến để đạt khoảng O(x2/3) thời gian. Với N≤1012 trong thi đấu, Lucy ngắn gọn hơn nhiều và đủ nhanh; Meissel-Lehmer chỉ đáng cài khi N tới 1013 trở lên.

Khi nào dùng kỹ thuật nào
Tình huống Kỹ thuật
Kiểm tra một số n≤1018 có nguyên tố không Miller-Rabin
π(v) hoặc giá trị hàm nhân tính cho mọi v≤107 Sàng Eratosthenes / sàng tuyến tính
Liệt kê số nguyên tố trong đoạn [L,R], R≤1012, R−L≤106 Sàng đoạn (segmented sieve)
Chỉ cần π(N), ∑p≤Np, hoặc theo lớp đồng dư, N≤1012 Lucy_Hedgehog (trang này)
N từ 1013 trở lên Meissel-Lehmer

Bài tập luyện

Luyện kỹ thuật
  • Số nguyên tố hay số đối xứng? (primepal) (Nâng cao) khởi động: cần π(n) cho mọi n tới khoảng 2·106, đủ nhỏ để dùng sàng thường với mảng tiền tố; bài giúp phân biệt khi nào sàng là đủ và khi nào mới cần Lucy.
  • Dãy số nguyên tố cùng nhau (kthcopr) (Kỳ cựu) đếm số ≤n không chia hết cho các ước nguyên tố của p bằng bao hàm, loại trừ: đúng là hàm ϕ(x,a) của Legendre (nền của Meissel-Lehmer) với tập số nguyên tố tùy ý, kết hợp chặt nhị phân.
  • Số may mắn kép (dblhappy) (Kỳ cựu) định lý Fermat quy bài về đếm số nguyên tố ≡1(mod4) trong [l,r], cộng thêm 1 nếu số 2 nằm trong đoạn (vì 2=12+12); lời giải gốc sàng bitset tới 3·108, nhưng Lucy với hai mảng theo lớp đồng dư mod 4 (phần Mở rộng) chạy tức thì và gần như không tốn bộ nhớ.

Trong ba bài trên, chỉ dblhappy áp dụng trực tiếp kỹ thuật Lucy; primepal (sàng thường) và kthcopr (bao hàm, loại trừ trên vài số nguyên tố) là bài liên quan để đối chiếu. Pool CTOJ hiện chưa có bài "thuần" đếm π(N) hoặc tổng số nguyên tố với N≥1010; khi có sẽ bổ sung vào đây.

Đề VOI
  • Xây dựng ma trận (voi22_matrix) (Chuyên gia) VOI 2022: điền tay vài hàng để thấy giá trị ô chỉ phụ thuộc vào thứ tự của ước nguyên tố nhỏ nhất của chỉ số hàng và cột; với m,n≤109, chạy Lucy riêng cho N=m (chỉ số hàng) và N=n (chỉ số cột): số chỉ số có cùng ước nguyên tố nhỏ nhất p≤N chính là lượng Lucy trừ đi ở bước p (cộng 1), còn mỗi số nguyên tố p>N tạo một lớp có đúng một phần tử.
Trang này có hữu ích không? Đăng nhập để trả lời

Bình luận

Không có bình luận tại thời điểm này.

gnatmake 12.2.0 a68g 3.1.2 nasm 2.16.1 as_x64 2.47 awk 1.3.4 gcc 16.2.0 dotnet 10.0.402 g++ 16.2.0 g++-themis 16.2.0 g++17 16.2.0 g++20 16.2.0 g++23 16.2.0 clang++ 22.1.8 dmd 2.113.0 dart 3.13.6 gforth 0.7.3 gfortran 15.3.0 go 1.27.3 groovyc 5.1.3 javac 26.0.2.1 node 26.12.1 julia 1.13.2 kotlinc 2.4.21 lean 4.35.0 sbcl 2.2.9 lua 5.5.1 nim 2.2.12 fpc 3.2.2 fpc-themis 3.2.2 perl 5.42.3 php 8.5.12 pike 8.0 swipl 9.0.4 pypy3 7.3.23 python3 3.15.1 racket 8.7 ruby 4.0.8 rustc 1.99.1 csc 5.3.0 ctoj-scratch 0.0.1 sed 4.9 swiftc 6.4.1 tclsh 8.6 bun 1.4.4 deno 2.9.8 v 0.5.2 zig 0.17.0