素数之埃氏筛法&欧拉筛法

给你一囗甜甜゛ 提交于 2020-02-05 03:32:11

素数的判断

  判断数n是否为一个素数,基本方法为从2开始向后枚举,若n不能被2,3,4,…,n-1整除,则n为素数,该判断方法的时间复杂度为O(n)O(n);更快的方法为,当枚举至n\sqrt n时即可判断是否为素数,该判断方法的时间复杂度为O(n)O(\sqrt n)

代码如下:

//写法1,其中sqrt函数位于头文件<math.h>,double sqrt(double a)
bool isPrime(int n){
	if(n <= 1) return false;  //特判
	int sqr = (int)sqrt(1.0 * n);  //根号n
	for(int i = 2; i <= sqr; i++){  //遍历2~根号n
		if(n % i == 0) return false;
	}
	return true;
}

//写法2,当n不接近int型上界时;或i以被定义为long long类型,不至于溢出
bool isPrime(int n){
	if(n <= 1) return false;
	for(int i = 2; i * i <= n; i++){
		if(n % i == 0) return false;
	}
	return true;
}

!!!注意溢出问题。

素数表的获取

基础方法:1~n枚举

  对1~n依次判断是否为素数,其枚举部分复杂度为O(n)O(n),判断是否为素数部分为O(n)O(\sqrt n),因此总复杂度为O(nn)O(n\sqrt n)
  当数据量小于10510^5时,该算法可行。

代码如下:

cosnt int maxn = 101;  //表长
int prime[maxn], pNum = 0;  //prime数组存放所有素数,pNum为素数个数
bool p[maxn] = {false};  //p[i] == true表示i为素数
void Find_Prime(){
	for(int i = 1; i < maxn; i++){ 
		if(isPrime(i) == true){
			prime[pNum++] = i;  //把i加入素数表
			p[i] == true;
		}
	}
}

埃氏(Eratosthenes)筛法

  对每个素数,划去其所有倍数,剩下的即为素数。时间复杂度为O(nloglogn)O(nloglogn)
  当数据量小于10710^7时可行,达到10710^7附近的难以满足。

举例如下:对2~15进行判断

  • 2是素数,筛去2的所有倍数,即4,6,8,10,12,14
      剩:2,3,5,7,9,11,13,15
  • 3是素数,筛去3的所有倍数,即9,15
      剩:2,3,5,7,11,13
  • 4已被筛去,非素数
  • ……

代码如下:

cosnt int maxn = 101;  //表长
int prime[maxn], pNum = 0;  //prime数组存放所有素数,pNum为素数个数
bool p[maxn] = {false};  //如果i为素数,则p[i] = false,否则为true
void Find_Prime(){
	for(int i = 2; i < maxn; i++){ 
		if(p[i] = false){  //若i为素数
			prime[pNum++] = i;  //把i加入素数表
			for(int j = i + i; j < maxn; j += i)  //筛去所有i的倍数
				p[i] == true;
		}
	}
}

:优化,倍数的筛去从j=iij=i*i开始,而不是i+ii+i开始,因为i(2i1)i*(2至i-1)在筛查2~i1i-1的倍数时已经被筛去。

欧拉(线性)筛法

  提出背景:由于埃氏筛法在划去倍数的过程中对于一个合数可能进行了多次重复划去,如对于合数30,当划去2,3,5倍数时均对其进行了操作,带来了不必要的重复,进而导致复杂度上升。
  主要思想:为避免合数的重复筛查,对每个数,使其被它的最小质因数筛查掉(最小质因数具有唯一性)。
  时间复杂度为O(n)O(n);当数据量小于10810^8时合适。

代码如下:

cosnt int maxn = 101;  //表长
int prime[maxn], pNum = 0;  //prime数组存放所有素数,pNum为素数个数
bool p[maxn] = {false};  //如果i为素数,则p[i] = false,否则为true
void Find_Prime(){
	for(int i = 2; i < maxn; i++){ 
		if(p[i] = false){  //若i为素数
			prime[pNum++] = i;  //把i加入素数表
		}
		for(int j = 0; (j < maxn) && (i * prime[j] < maxn); j++){  //筛去基于i的非2最小质因数倍数
				p[i * prime[j]] == true;  //筛去i * prime[j]
				if(i % prime[j] == 0) break;  //避免冗余筛查
		}
	}
}

一些问题

  • 为何该算法遍历了所有1~n?
      虽然筛查的内循环部分只遍历了一部分数据,但外循环从1~n进行了遍历并用 if 判断了是否为素数。
  • 为何该算法避免了冗余筛查的出现?
      因为外循环数ii不断增加,内循环作为筛查部分每次筛查掉的数为iprime[j]i*prime[j],由于prime[j]prime[j]为大于等于2的质数,因此即筛查掉的数均为比ii大的数。
      语句i%prime[j]==0i\%prime[j]==0作为终止内循环条件,避免了冗余筛查的作用在于:ii此时可写为i=kprime[j]i=k*prime[j],此时若未终止循环,则下一步将对iprime[j+1]i*prime[j+1]进行筛查,而此时iprime[j+1]=kprime[j]prime[j+1]i*prime[j+1]=k*prime[j]*prime[j+1],又因为prime[j]prime[j]显然小于prime[j+1]prime[j+1],故prime[j]prime[j]必为其最小质因数,因此数iprime[j+1]i*prime[j+1]必将在未来ii增加至某个值hh时被hprime[j]h*prime[j]筛查掉,此时iprime[j+1]=hprime[j]i*prime[j+1]=h*prime[j]

参考资料

[1]. 《算法笔记》P160-162
[2]. 数论_欧拉筛法

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!