埃氏筛法
质数筛法有很多种,这里介绍最简单的一种,埃氏筛法。
一个数如果它有除 1 和自己以外的约数,那么它一定不是一个质数,因此考虑枚举 1−>N 中每个数的倍数,并标记它们。这些被标记的就一定不是质数了,相反,剩下那些没有被标记的,就是我们“筛选”出来的质数。
以 n=50 为例:先假设 [2,n] 所有数都是质数。
2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50
大于 2 的 2 的倍数一定不是质数,标记 4,6,8...50 。
大于 3 的 3 的倍数一定不是质数,标记 6,9,12...48 。
大于 5 的 5 的倍数一定不是质数,标记 10,15...50 。
大于 7 的 7 的倍数一定不是质数,标记 14,21...49 。
一直这样下去, 最终留下来未被标记的就都是质数了。
2,3,5,7,11,13,17,19,23,29,31,37,41,43,47。
可以结合下面这两张动图理解:
你会计算这个算法的复杂度吗?
循环的总次数小于:
n/1+n/2+n/3+....n/n
根据欧拉1734年得到的结论:
11+12+13+...1n=ln(n+1)+γ
整个算法的复杂度为 O(nlog(n)) ,其中 γ≈0.577218 ,也被称为欧拉常数。
当范围在int范围内:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=5000000;
long prime[maxn]; // 存储一个个确定为质数的数
bool is_prime[maxn+1]; // 标记范围内所有数
int p = 0;
int sieve(int n)
{
p = 0;
for(int i=0;i<=n;i++)
is_prime[i]=true; // 所有数先标记为true
is_prime[0] = is_prime[1] = false; // 把数字0,1标记为质数
for(int i=2;i<=n;i++)
{
if(is_prime[i]) // 如果这个数没有被标记为false
{
prime[p++]=i; // 用prime数组存起来这个数,既存起了质数,又用p表示了质数个数
for(int j=i*i;j<=n;j+=i) // 这里没有优化时的写法是for(int j=2*i; j<=n; j++)。
//因为小于j(即i^2)内的合数都因为(根号j)(即i)内有更小的j的的因数而被排除
// 比如3^2 = 9,为什么不算2*3 = 6呢, 因为6<9,所以6因为3以内有更小的因数而直接被排除
is_prime[j]=false;
}
}
return p; // 返回质数个数
}
int main()
{
int n;
while(~scanf("%d",&n))
{
printf("质数个数是: %d\n",sieve(n));
printf("质数有:\n");
for (int i = 0; i<p; i++)
{
printf("%d ", prime[i]);
printf("\n\n");
}
}
system("pause");
}
当范围超过了int
static const int N = 1e7;
bool is_prime[N]; // 判断是否是素数
ll prime[N]; // 存储素数
ll sieve(ll num)
{
int inx = 0;
for (int i = 0; i<=N; i++)
is_prime[i] = true;
is_prime[0] = is_prime[1] = false;
int MIN = (num > N) ? N : num;
for (ll i = 2; i<=MIN; i++)
{
if (is_prime[i])
{
prime[inx++] = i;
for (ll j = i*i; j<=num; j+=i)
is_prime[j] = false;
}
}
return inx;
}
int gcd(int inx) // 此处由于传进来都是质数,所以直接相乘即为gcd
{
int res = 1;
for (int i = 0; i<inx-1; i++)
res *= prime[i];
return res;
}
void C3()
{
ll num; // 输入数
int p; // 最小公约数
cin >> num;
int inx = sieve(num); // 筛选素数
cout << gcd(inx) << endl;
}
建议做题 :
质数中的质数(质数筛法)
如果一个质数,在质数列表中的编号也是质数,那么就称之为质数中的质数。例如:3 5分别是排第2和第3的质数,所以他们是质数中的质数。现在给出一个数N,求>=N的最小的质数中的质数是多少(可以考虑用质数筛法来做)。
输入格式
输入一个数N(N <= 10^6)
输出格式
输出>=N的最小的质数中的质数。
输入样例
20
输出样例
31
此题使用埃氏筛法
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool f[1001001];
int prime[80000], cnt = 0;
void sieve(int x) {
for (int i = 2; i <= x; ++i) {
if (!f[i]) {
prime[cnt++] = i;
for (ll j = (ll)i * i; j <= x; j += i)
f[j] = 1;
}
}
}
int main() {
int n, pos;
cin >> n;
sieve(n + 1000);
pos = upper_bound(prime, prime + cnt, n - 1) - prime;
pos = upper_bound(prime, prime + cnt, pos) - prime;
cout << prime[prime[pos] - 1] << endl;
return 0;
}
████████████████████████████████████████████████