# Sàng Linear
---
**Tác giả : [Dương Nguyên Khánh](https://github.com/kduongnguyen07)</a>**
---
###### tags:`Number Theory`,`Sieve`,`Prime Numbers`
# **Mở đầu**
Số nguyên tố từ lâu nay luôn là một loại số có xuất hiện nhiều trong các cuộc thi và có áp dụng lớn đến cuộc sống
## Một số kỹ thuật kiểm tra số nguyên tố
```
- Kiểm tra Rabin-Miller
- Sử dụng các loại sàng
- Chạy trâu
```
# Cơ chế hoạt động
## ***Chạy trâu***
Với mọi số nguyên x trong đoạn [0,n]. Ta đếm số ước d của x trong đoạn [0,x]. Nếu x có đúng 2 ước thì x là số nguyên tố
```C++
#include <bits/stdc++.h>
using namespace std;
const int LIM = 1e6;
bool checkPrime(int n)
{
int cnt = 0;
for (int d = 1; d <= n; ++d)
if (n % d == 0)
++cnt;
return cnt == 2;
}
int cntPrime;
bool isPrime[LIM + 1];
void sieve(int n)
{
cntPrime = 0;
for (int x = 0; x <= n; ++x)
{
if (checkPrime(x))
{
++cntPrime;
isPrime[x] = true;
}
}
}
```
## ***Sàng Eratosthenes***
Nhận thấy rằng các số có nhiều ước thì sẽ bị đánh dấu nhiều lần. Vì khi x là hợp số, thì với mọi ước v của x, thì bội kx của x cũng là bội của v. Nên để hạn chế điều đó, ta chỉ đánh dấu các bội của x khi x là số nguyên tố.
```C++
#include <bits/stdc++.h>
using namespace std;
const int LIM = 1e6;
int cntPrime;
bool isPrime[LIM + 1];
void sieve(int n)
{
memset(isPrime, true, sizeof(isPrime[0]) * (n + 1));
isPrime[0] = isPrime[1] = false;
for (int p = 2; p * p <= n; ++p) if (isPrime[p])
for (int x = p + p; x <= n; x += p)
isPrime[x] = false;
cntPrime = 0;
for (int p = 0; p <= n; ++p) cntPrime += isPrime[p];
}
```
## ***Sàng Eratosthenes cải tiến bằng Linear Search***
Nhưng, với cách trên thì mình vẫn phải đánh dấu nhiều lần các số có nhiều ước nguyên tố.
Ta gọi lpf[x] là ước nguyên tố nhỏ nhất của x
(viết tắt của lowest_prime_factor)
Khi này mọi số x>1
đều có thể được biểu diễn bằng x=k×lpf[x]. Mà từ định nghĩa ta có lpf[x]≤lpf[k]
**Cài đặt**
```C++
#include <bits/stdc++.h>
using namespace std;
const int LIM = 1e6; //giới hạn đến 10^6
vector<int> prime;
vector<int> lpf;
void sieve(int n)
{
prime.assign(1, 2);
lpf.assign(n + 1, 2);
for (int x = 3; x <= n; x += 2)
{
if (lpf[x] == 2) prime.push_back(lpf[x] = x);
for (int i = 0; i < prime.size() && prime[i] <= lpf[x] && prime[i] * x <= n; ++i)
lpf[prime[i] * x] = prime[i];
}
}
/*
Cách này khi gọi cout<<prime.size()
thì sẽ in ra được số phần tử là số nguyên tố đến N
*/
```
**Nếu muốn in ra tất cả các phần tử là số nguyên tố nhỏ hơn hoặc bằng N**
```C++
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N = 10000000;
vector<int> lp(N+1);
vector<int> pr;
int main()
{ cin>>N;
for (int i=2; i <= N; ++i) {
if (lp[i] == 0) {
lp[i] = i;
pr.push_back(i);
}
for (int j = 0; i * pr[j] <= N; ++j) {
lp[i * pr[j]] = pr[j];
if (pr[j] == lp[i]) {
break;
}
}
}
for (auto x:pr)
{
cout<<x<<" ";
}
}
```
Độ phức tạp thời gian : O(N)