埃氏筛与欧拉筛

1.埃氏筛
思想:任意合数都可以表示成几个素数的乘积,那么我们可以每找到一个素数,就将他的倍数都标记(代表这个数是合数)。2是最小的素数,所以我们从2开始标记。时间复杂度为:O(n*logn)
代码如下:

const int maxn=1e5+10;
bool flag[maxn];
int prime[maxn];
int pri_cnt=0;
void get_prime(int n){
	for(int i=2;i<=n;i++){
		if(!flag[i]){
			prime[++pri_cnt]=i;
			for(int j=i*i;j<=n;j+=i){
				flag[j]=1;
			}
		}
	}
}

  对于上面j=i*i可能会有人有疑问,按照上面的思路,初始值j不应该等于2*i吗,因为区间i*(2~ i-1)2~i-1时都已经被筛去,所以从i * i开始.
2.欧拉筛
思想:由埃氏筛我们已经知道筛法的核心就是标记,但是我们可以发现:即便我们做了一点优化,随着数据量的增长,一些拥有多个素因子的合数会被重复标记。我们可以想到用唯一素数来标记合数,这样每个合数都只会被标记一次,因此时间复杂度降到O(n);
代码如下:

const int maxn=1e5+10;
int v[maxn],prime[maxn];//数组v记录每个数的最小质因子 
int pri_cnt=0;//pri_cnt记录质数的个数 
void prime_table(int n){
	for(int i=2;i<=n;i++){
		if(v[i]==0){//i是质数 
			v[i]=i;	
			prime[++pri_cnt]=i;
		}
		for(int j=1;j<=pri_cnt;j++){
			//如果(i*prime[j])有比prime[j]更小的因子,或者超出n的范围,停止循环
			if(prime[j]>v[i] || prime[j]>(n/i)) break;
			//prime[j]是合数i*prime[j]的最小因子 
			v[i*prime[j]]=prime[j];
		} 
	} 
}

  prime[j]>v[i]表示当前的素数prime[j]比累乘因子i的最小素因子大,这就代表合数i*prime[j]应该被i的最小素因子v[i]筛掉,而不是被当前的素数prime[j]筛掉,举个例子:12=4x3;当i=4 prime[j]=3时,12本应该被2筛掉,也就是被4的最小素因子2(v[4]=2)筛掉,轮不到3来筛,所以跳出循环。
  prime[j]>(n/i)prime[j]*i>n的意思,用除法不用乘法是为了防止溢出(当数据量很大的时候,会爆int)

发布了10 篇原创文章 · 获赞 7 · 访问量 158

猜你喜欢

转载自blog.csdn.net/qq_44204959/article/details/104763085
今日推荐