质数筛
在算法题中质数判断是经常出现的问题,下面介绍几种算法:
1、朴素判断(改进)
首先是最容易理解,也是时间复杂度最高的算法 -> 单次判断O(√n)
先放代码
#include <cmath>
int PrimeChoose(int n)
{
int i;
int is_prime = 1;
if (n == 1)
return 0;
for(i=2;i<=sqrt(n);i++)
if (n % i == 0)
{
is_prime = 0;
break;
}
return is_prime;
}
众所周知,1不是质数,
而合数必然能由一个 >= sqrt(n) 与一个 <= sqrt(n)的数字相乘得到,
所以我们只需要从2到sqrt(n)挨个判断n能否被其整除就行了。
当进行少量数判断时可以使用这种方法,
在测试量很大时,这种方法显然是不可取的。
当我们需要判断1~n范围内数字是否为质数可以用以下两种方法:当n很大时效率便非常高
2、埃氏筛(埃拉托斯特尼筛法)
时间复杂度O(n ln ln n)
这种算法还是比较容易理解的
依据:
1).若X是素数,则X的倍数都不是素数
2).若X不是素数,则X肯定在[1,X)之间被筛选掉。即存在Y,使得k*Y=X,然后Y根据推理1) 判定X为非素数。
由此我们可以得到 :若X未被事先筛选掉则X为素数。
那么下面我们就可以写代码了!!
bool prime[MAXN] = {};
void Prime(int n)
{
for (int i = 2; i <= n; i++)
{
prime[i] = true;
}
for (int i = 1; i * i <= n; i++)
{
if (prime[i])
{
for (int j = i * i; j <= n; j += i) //j从i²开始判断更高效,因为之前2*i,3*i等等已经被筛除了
{
prime[j] = false;
}
}
}
}
我们可以发现用此筛法筛选时,一个数可能被多次筛选,例如:12被2筛除又被4筛除。此算法显然有改进空间。
3、欧式筛(线性筛)
这个算法的时间复杂度达到了惊人的O(n),也是我已知最高效的筛法;
欧式筛保证了每一个合数都只被其最小的质因子筛除;
先上代码
int prime[MAXN];
bool vis[MAXN] = {};
int cnt=0;
void Euler_prime(int n)
{
vis[1] = ture;
for(int i=2;i<=n;++i)
{
if(!vis[i])
{
prime[cnt++]=i;
}
for(int j=0;j<cnt;++j)
{
if(i*prime[j]>n)//判断是否越界
break;
vis[i*prime[j]]=true;//筛数
if(i%prime[j]==0)
break;
}
}
}
筛除的原理是:
用当前的 i 去寻找 <= i 的质数 两者相乘所筛除的数其最小质因子一定是该质数。
下面的代码保证了该原理的正确性,也是O(n)的关键。
这段代码是核心中的核心,真的NB!!!
if(i%prime[j]==0)
break;
当i是prime[j]的整数倍时,记 k = i / prime[j],那么 i * prime[j+1] 就可以变为 (k * prime[j+1]) * prime[j],
这说明 i * prime[j+1] 是 prime[j] 的整数倍,不需要现在筛出,因为在之后筛除过程中i * prime[j+1] 这个合数一定会被prime[j]筛除,
prime[j]之后的所有素数同理,所以break跳出循环。
4、区间筛
如果要筛[a,b)中的质数,
因为b以内的合数的最小质因数一定不超过sqrt(b),
如果有sqrt(b)以内的素数表的话,就可以把埃式筛法运用在[a,b)上了。
也就是说,先分别做好[2,sqrt(b))的表和[a,b)的表,然后从[2,sqrt(b))的表中筛得素数的同时,也将其倍数从[a,b)的表中划去,
最后剩下的就是区间[a,b)内的素数了。
在制作[a,b)的表我们用到下标偏移,用来减小内存开销防止MLE。
放上代码
bool is_prime_small[MAXN1]; //区间[2,sqrt(b))
bool is_prime[MAXN2]; //区间[a,b)
//对区间[a,b)内的整数执行筛法,is_prime[i-a]=true <=> 表示i是素数(下标偏移了a)
void segment_sieve(long long a, long long b)
{
long long i, j;
for ( i = 0; i * i < b; i++) //对[2,sqrt(b))的初始化全为质数
is_prime_small[i] = true;
for ( i = 0; i < b - a; i++) //对下标偏移后的[a,b)进行初始化
is_prime[i] = true;
for ( i = 2; i * i < b; i++)
{
//筛选[2,sqrt(b))
if (is_prime_small[i])
{
for ( j = 2 * i; j * j < b; j += i)
is_prime_small[j] = false;
//(a+i-1)/i 得到最接近a的i的倍数,最低是i的2倍,然后筛选
for (j = (2LL>(a + i - 1)/i? 2LL: (a + i - 1) / i) * i; j < b; j += i)
is_prime[j - a] = false;
}
}
}
当a == 1时,a不会被筛除 >>>>> 这种情况也用不到这个算法。。。。。

浙公网安备 33010602011771号