NWPU-ICPC-数学知识-1.筛质数

筛质数

素数

定义

如果存在一个整数k,使得a=kd,则称d整除a,记作d|a,称a是d的倍数,如果d>0,称d是a的约数。特别地,任何整数都整除0。

显然大于1的整数a可以被1和a整除,如果除此之外a没有其他约数,则称a是素数,又称质数。任何一个大于1的整数如果不是素数,也就是有其他约数,就称为合数。(1既不是质数也不是合数)。

素数奇数函数

π ( x ) : 小 于 等 于 x 的 素 数 的 个 数 \pi(x):小于等于x的素数的个数 π(x)x。随着x的增大,可以近似为: π ( x ) ≈ x l n ( x ) \pi(x)≈\frac{x}{ln(x)} π(x)ln(x)x

埃氏筛法

如果x是合数,那么x的倍数也一定是合数。利用这个结论,可以避免许多不必要的检测。

如果从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是素数了。

int Eratosthenes(int n) {
  int p = 0;
  for (int i = 0; i <= n; ++i) is_prime[i] = 1;
  is_prime[0] = is_prime[1] = 0;
  for (int i = 2; i <= n; ++i) {
    if (is_prime[i]) {
      prime[p++] = i;  // prime[p]是i,后置自增运算代表当前素数数量
      if ((long long)i * i <= n)
        for (int j = i * i; j <= n; j += i)
          // 因为从 2 到 i - 1 的倍数我们之前筛过了,这里直接从 i
          // 的倍数开始,提高了运行速度
          is_prime[j] = 0;  // 是i的倍数的均不是素数
    }
  }
  return p;
}

时间复杂度是 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)

线性筛法

初始时,假设全部都是素数,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数
把这些合数都筛掉。但仔细分析能发现,这种方法会造成重复筛除合数,影响效率。

比如30,在 i = 2 的 时 候 , k = 2 ∗ 15 i=2的时候,k=2*15 i=2k=215筛了一次;在 i = 5 , k = 5 ∗ 6 i=5,k=5*6 i=5k=56 的时候又筛了一次。

利用每个合数必有一个最小素因子。每个合数仅被它的最小素因子筛去正好一次。所以为线性时间。
NWPU-ICPC-数学知识-1.筛质数_第1张图片
这样就能让每个合数都只被标记一次,那么时间复杂度就可以降到 O ( n ) O(n) O(n)了。

int Prime[maxn],vis[maxn];
void GetPrime(){
	for(int i=2;i<=N;i++){
		if(!vis[i]){
			Prime[++Prime[0]]=i;
		}
		for(int j=1;j<=Prime[0]&&Prime[j]*i<=N;j++){
			vis[i*Prime[j]]=1;
			if(i%Prime[j]==0){
				break;
                // i % Prime[j] == 0
                // 换言之,i 之前被 Prime[j] 筛过了
                // 由于 Prime 里面质数是从小到大的,所以 i 乘上其他的质数的结果一定也是
                // Prime[j] 的倍数 它们都被筛过了,就不需要再筛了,所以这里直接 break
                // 掉就好了
			}
		}
	}
} 

代码中体现在:
if(i%prime[j]==0)break;
prime[]中的素数是递增的,当 i 能整除 prime[j],那么 i ∗ p r i m e [ j + 1 ] i*prime[j+1] iprime[j+1] 这个合数肯定被 prime[j] 乘以某个数筛掉。
因为i中含有prime[j], prime[j] 比 prime[j+1] 小。接下去的素数同理。所以不用筛下去了。
在满足i%prme[j]==0这个条件之前以及第一次满足改条件时,prime[j]必定是 p r i m e [ j ] ∗ i prime[j]*i prime[j]i的最小因子

你可能感兴趣的:(数学,NWPU-ICPC专题培训)