质数判断|质数筛法

质数

概述

质数(prime number,又称素数):

指在大于1的自然数中,除了1和该数自身外,无法被其他自然数整除的数(也可定义为只有1与该数本身两个正因数的数)。

否则称为合数(又称合成数)。

1既不是质数也不是合数,2是最小的质数且是唯一的偶质数

质数判断

试除法 O ( n ) O(\sqrt{n}) O(n )

思想:根据定义,可以从2~n-1依次试除,判断n是否有约数(若有则n不是质数)

优化:假设 d d d 可以整除 n n n,那么它们的商 n / d n/d n/d 也能整除 n n n。假设两者的关系是 d < = n / d d<=n/d d<=n/d,即 d 2 < = n d^2<=n d2<=n ,那么 d < = n d<=\sqrt n d<=n 。所以每次判断只需要判断到 n \sqrt{n} n 即可。

时间复杂度:优化后时间复杂度为 O ( n ) O(\sqrt{n}) O(n )

bool isprime(int n) 
{
	for (int i = 2; i <= n / i; i++)//也有的会写为i*i<=n,不过存在溢出风险,使其变为负数,影响结果判断。最好写为i<=n/i,不存在溢出风险
		if (n % i == 0) return false;
	return true;
}

质数筛法

朴素筛法 O ( n n ) O(n\sqrt{n}) O(nn )

背景:质数相关的问题,如果用传统的遍历法的话,一次只能求解一个( O ( n ) O(\sqrt n) O(n )

过程: 试除法将素数保存下来即可

int prime[N], cnt;
for (int i = 2; i <= 100; i++)
    if (isprime(i))prime[++cnt] = i;
埃氏筛法 O ( n l o g n ) O(nlogn) O(nlogn)

思想:从2开始任何整数 x 的倍数都不是质数。

过程:初始时,先假设所有数都是质数,从2开始枚举,并标记每个数的倍数为合数,若枚举到一个数未被标记,说明该数就是质数。

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

int prime[N], cnt;
bool st[N];
void GetPrimes(int n) 
{
    memset(st, true, sizeof(st));
    for (int i = 2; i <= n; i++) 
    {
        if (st[i]) prime[++cnt] = i;
        for (int j = i + i; j <= n; j += i) st[j] = false;//筛掉该数的倍数
    }
}

埃氏筛相关

1、埃拉托斯特尼选筛法,简称埃氏筛,也有人称素数筛。

2、算术基本定理 (又称唯一分解定理),任何一个大于1的自然数N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积。也就是说,合数一定是某个质数的倍数。

优化:埃氏筛法是不管是合数还是质数都直接筛,会有许多重复的操作。根据算术基本定理,只筛掉质数的倍数。

时间复杂度 O ( n l o g l o g n ) O(nloglogn) O(nloglogn) 该算法实现简单,效率已经非常接近与线性(n <= 106

int prime[N], cnt;
bool st[N];
void GetPrimes(int n) 
{
    memset(st, true, sizeof(st));
    for (int i = 2; i <= n; i++)
    {
        if (!st[i])continue;//合数
        prime[++cnt] = i;
        for (int j = i + i; j <= n; j += i) st[j] = false;//筛掉质数倍数
        //遍历范围内的i的倍数 可从i*i开始,可以减少重复筛选(
    }
}
线性筛法 O ( n ) O(n) O(n)

欧拉筛,也被称作线性筛

背景:埃氏筛中一些合数被重复筛选(如:6会被2和3筛选),这样会有许多重复操作

思想:在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的

时间复杂度 O ( n ) O(n) O(n)当数据规模在 106 时,线性筛法和埃式筛法效率差不多,但是当数据规模在 107 时线性筛法的效率大约是埃式筛法2倍

int prime[N], cnt;
bool st[N];
void GetPrimes(int n)
{
	memset(st, true, sizeof(st));
	for (int i = 2; i <= n; i++)
	{
		if (st[i])prime[++cnt] = i;
		for (int j = 1; prime[j] <= n / i; j++)
		{
			st[prime[j] * i] = false;
			//(prime[j] * i)为要筛掉的合数
			//(prime[j] * i)的最小质因数 = min(prime[j]的最小质因数, i的最小质因数)
			//质数prime[j]的最小质因数是它本身
			//当i% prime[j] != 0时,i的最小质因数>prime[j],所以(prime[j] * i)的最小质因数 = prime[j]
			//当i% prime[j] == 0时,i的最小质因数=prime[j],所以(prime[j] * i)的最小质因数 = prime[j]
			if (i % prime[j] == 0)break;
			//若i% prime[j] == 0后继续枚举质数表,那么prime[j+1]>prime[j],即prime[j+1]不是i的最小质因子
			//自然prime[j+1]不是(prime[j]*i)的最小质因子
		}
	}
}

你可能感兴趣的:(数论,算法)