写在前面
前段时间一直在准备期末考试,很长时间没有打理自己的博客,还好从这周开始,就是三周的小学期,时间还算富裕,争取找到之前的节奏,继续坚持下去!
这两种素数的筛选方法,第一种方法在之前的博客中使用过,但是只是顺便的提了一下,这里加了一个Euler筛选法,相对于Eratosthenes筛选法,时间复杂度更低一些
主要的参考文献:《线性筛法与积性函数》贾志鹏
Eratosthenes筛法
Eratosthenes筛选法用到的主要思想:当一个素数被发现时,那么是这个素数倍数的数一定不再是素数,运用这个思路就可以得到时间复杂度为O(N log(log(N)))的算法
void Prim(int n)
{
//Eratosthenes筛选法
memset(check,false,sizeof(check));
int tot = 0;
for(int i = 2;i <= n;i ++)
if(!check[i])
{
prim[tot++] = i;
for(int j = 2*i;j <= n;j += i)
check[j] = true;
}
}
Euler筛法
上面的Eratosthenes筛选法,我们发现一个非素数可能会有很多次被判断,比如12,可以有2*6排除,3*4排除
那么我们为了去除这种重复,我们规定,只能使用一个数的最小质因数来排除这个数字,那么12就只能由2*6排除,24只能由2*12排除掉
那么我们怎么实现上面的要求呢?先看程序,后面解释这个内容
void Euler_prim(int n)
{
//欧拉筛选法 避免上面筛选法中的重复筛选
memset(check,false,sizeof(check));
int tot = 0;
for(int i = 2;i <= n;i ++)
{
if(!check[i]) prim[tot ++] = i;
for(int j = 0;j < tot;j ++) //遍历已经找到的素数
{
if(i * prim[j] > n) break; //后面相乘已经超出 n 的范围,没有查找的必要了
check[i * prim[j]] = true; //表示这个数字不是素数
if(i % prim[j] == 0) break;
}
}
}
上面的if(i % prim[j] == 0 ) break; 是完成上面要求的重要条件
我们首先知道一个合数一定可以表示成素数的乘积,那么也一定可以表示成他的最小质因数乘以另外一个数,已知prim数组中的素数是依次递增的,当i满足i%prim[j] == 0的时候,假如我们继续执行,那么下一个要判断为不是质数的数为:prim[j+1]*i ,但是我们发现这个数的最小质因数一定是prim[j] ,因为这里的i满足i%prim[j] == 0 ,prim[j]是他的最小质因数,所以这里就和我们上面的要求有矛盾,当我们遇到后面x满足:prim[j] * x = prim[j+1]*i时,又会重新判断一次这个数字
也就是说:当我们遇到上面这个条件就要break ,时间复杂度变为O(N)
写在后面
这个算法真的是理解了很长的时间,当然上面的理解是我个人的理解,如果有理解不当的地方,欢迎大家在评论区留言评论。