质数和合数
- ⼀个⼤于1的⾃然数,除了1和它⾃⾝外,不能被其他⾃然数整除的数叫做质数;否则称为合数。其中,质数⼜称素数。
规定1 既不是质数也不是合数。
试除法判断质数
- 对于⼀个数x ,根据定义,可以从
[2, x - 1]
⼀个⼀个尝试,判断x 能否被整除。
但是,没有必要每⼀个都去判断。因为a如果是x的约数,那么x/a也是x的约数。
因此,我们仅需判断较⼩的a是否是x的约数,没有必要再去看看x/a 。
那么,仅需枚举到 x \sqrt{ x } x即可。
bool isprime(int x)
{
if(x <= 1) return false; // ⼩于等于 1 的数不考虑
// 试除法判断是否是质数 - 只需枚举到 sqrt(x)
for(int i = 2; i <= x / i; i++) // 防溢出的写法
{
if(x % i == 0) return false;
}
return true;
}
时间复杂度:
枚举到
n
\sqrt{ n }
n ,因此时间复杂度为O(
n
\sqrt{ n }
n)
P5736 【深基7.例2】质数筛 - 洛谷
读⼀个判断⼀个即可
#include <bits/stdc++.h>
using namespace std;
bool isprime(int x)
{
if (x <= 1) return false;
for (int i = 2; i <= x / i; i++)
{
if (x % i == 0) return false;
}
return true;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n; cin >> n;
while (n--)
{
int x; cin >> x;
if (isprime(x)) cout << x << " ";
}
return 0;
}
筛质数
引⼊
上⼀个专题学习了如何判断⼀个数是否是质数,如果此时想知道[1, n]
中有多少个素数呢?或者是[1, n]
中的素数⾥⾯,第k个素数是多少?
- ⼀个⾃然的想法就是从2 开始,依次向后对每⼀个⾃然数进⾏⼀次质数检验。
但是这种解法相对暴⼒,我们这⾥介绍两种⽅法,能够快速地将[1, n]
中的素数全部记录下来。
埃⽒筛法
算法思想:
- 对于任意⼀个⼤于1 的正整数,那么它的k(k > 1) 倍就是合数。
因此,如果我们从⼩到⼤考虑每个数,然后同时把当前这个数的所有倍数记为合数,没有被标记的数就是素数。
⼩优化: - 找到⼀个质数x 之后,可以从该数的x 倍向后筛,因此⼩于x 的倍数⼀定被之前筛过了
bool st[N]; // 当前这个数有没有被筛掉
int p[N]; // 记录质数
int cnt; // 统计质数个数
// 埃⽒筛
void get_prime()
{
for(int i = 2; i <= n; i++)
{
if(!st[i]) // 没有被标记,说明是质数
{
p[++cnt] = i; // 记录这个质数
// 从 i*i 开始,因为⼩于 i 的倍数已经被划掉了
for(LL j = i * i; j <= n; j += i) // 筛掉这个质数的倍数
{
st[j] = true;
}
}
}
}
时间复杂度:
埃⽒筛的时间复杂度为:O(n log n) 。
关于时间复杂度,⽹上各个地⽅都有详细的证明。因为很⿇烦,⽽且涉及积分的知识,这⾥就不再赘述。
线性筛法
线性筛法,⼜称欧拉筛法。算法思想:
- 在埃⽒筛法中,它会将⼀个合数重复多次标记。如果能让每个合数都只被标记⼀次,那么时间复杂度就可以降到O(n) 了。
我们的做法是,让每⼀个合数被它的最⼩质因数筛掉
从前往后遍历每一个数i
- 用i的质数倍去筛
- 如果i是质数的倍数时,停止
int n, q;
bool st[N];
int p[N], cnt;
// 欧拉筛
void get_prime()
{
for(int i = 2; i <= n; i++)
{
if(!st[i]) p[++cnt] = i; // 如果没标记过,就是质数
// 枚举所有的质数
for(int j = 1; 1ll * i * p[j] <= n; j++)
{
st[i * p[j]] = true;
if(i % p[j] == 0) break;
/*
这个判定条件能让每⼀个合数被⾃⼰的最⼩质因数筛掉。
1. 如果 i 是合数,枚举到最⼩质因数的时候跳出循环
2. 如果 i 是质数,枚举到⾃⾝时跳出循环
注意,在筛的过程中,我们还能知道 p[j] 是 i 的最⼩质因数
*/
}
}
}
时间复杂度:
每个数只会被⾃⾝最⼩的质因数筛掉⼀次,时间复杂度为O(n)
P3383 【模板】线性筛素数 - 洛谷
埃⽒筛
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e8 + 10;
int n, q;
bool st[N];
int p[N], cnt;
void get_prime()
{
for (LL i = 2; i <= n; i++)
{
if (!st[i])
{
p[++cnt] = i;
for (LL j = i * i; j <= n; j += i)
{
st[j] = true;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> q;
get_prime();
while (q--)
{
int k; cin >> k;
cout << p[k] << endl;
}
return 0;
}
线性筛
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e8 + 10;
int n, q;
bool st[N];
int p[N], cnt;
void get_prime()
{
for (LL i = 2; i <= n; i++)
{
if (!st[i]) p[++cnt] = i;
for (int j = 1; i * p[j] <= n; j++)
{
st[i * p[j]] = true;
if (i % p[j] == 0) break;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> q;
get_prime();
while (q--)
{
int k; cin >> k;
cout << p[k] << endl;
}
return 0;
}
P1835 素数密度 - 洛谷
- 埃⽒筛+线性筛。
先筛出[1, r]
之间的质数,然后⽤这些质数去筛[l, r]
区间的质数。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
int l, r;
bool st[N];
int p[N], cnt;
bool ret[N];
void get_prime()
{
int n = sqrt(r);
for (int i = 2; i <= n; i++)
{
if (!st[i]) p[++cnt] = i;
for (int j = 1; 1ll * i * p[j] <= n; j++)
{
st[i * p[j]] = true;
if (i % p[j] == 0) break;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> l >> r;
get_prime();
l = l == 1 ? 2 : l;
for (int i = 1; i <= cnt; i++)
{
LL x = p[i];
for (LL j = max(x * 2, (x + l - 1) / x * x); j <= r; j += x)
{
ret[j - l] = true;
}
}
int sum = 0;
for (int i = l; i <= r; i++)
{
if (!ret[i - l]) sum++;
}
cout << sum << endl;
return 0;
}
UVA543 Goldbach’s Conjecture - 洛谷
先筛⼀下质数,然后在筛出来的质数中,找出两个数的和等于给定的数
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
bool st[N];
int p[N], cnt;
void get_prime()
{
int n = 1e6;
for (int i = 2; i <= n; i++)
{
if (!st[i]) p[++cnt] = i;
for (int j = 1; 1ll * i * p[j] <= n; j++)
{
st[i * p[j]] = true;
if (i % p[j] == 0) break;
}
}
}
void solve(int x)
{
for (int i = 2; i <= cnt; i++)
{
if (!st[x - p[i]])
{
printf("%d = %d + %d\n", x, p[i], x-p[i]);
break;
}
}
}
int main()
{
get_prime();
int x;
while (cin >> x, x)
{
solve(x);
}
return 0;
}