求素数

   在算法竞赛中很多时候要用到素数,因此快速有效地得到素数就是一个重要技能了。本蒟蒻在此列举几种求 n n n以内素数的方法。

1.暴力(傻瓜算法)

  这是最没用的算法,时间复杂度太高:枚举 n n n以内的每一个数,再用试除法判断该数是否为素数。

code
//Maxn 为数组q的大小。
bool q[Maxn}
bool check(int n)
{

	for(int i = 0 ; i * i < n ; i ++)
		if(n % i == 0)
			return false;
	return true;
}//试除法
void make_q(int n)
{
	for(int i = 2 ;i <= n ; i++)//1和0既不是质数也不是合数,应单独判断
		if(check(i))
			q[i] = true
}

2.Eratosthenes筛法

  最常用的素数筛,其原理是每枚举到一个素数就将其倍数全标记为合数,那么没有统计到的就是素数。计算到 n \sqrt{n} n 时就可以筛出小于 n n n的所有素数。

code

bool q[Size];//Size为n的最大值。

void make_q(int n)
{
    q[1] = false;
    q[0] = false;
    for(int i = 2 ; i * i <= n ; i++)//初始化。
	q = true;
    for(int i = 2 ; i * i <= n ; i++)
    {
    	for(int j = i * i ; j <= n ; j += j)
    	{
    	    q[j] = false;
    	}
    }
}

复杂度 O ( n × log ⁡ n ) O(n\times\log{n}) O(n×logn)
  但在这种情况下还是会有大量点被重复标记,所以可以选择只筛素数的倍数,这与筛所有数的倍数是等效的(参照算术基本定理)。

code

bool q[Size];

void make_q(int n)
{
    q[0] = false;
    q[1] = false;
    for(int i = 2 ; i * i <= n ; i++)
    {
    	if(q[i])
    	{
    	    for(int j = i * i ; j < n ; j++)
    	    {
    	    	q[j] = true;
    	    }
    	}
    }
}

复杂度 O ( n × log ⁡ log ⁡ n ) O(n\times\log{\log{n}}) O(n×loglogn)


3.线性筛法(欧拉筛)

  埃氏筛虽然复杂度接近线性但还是有一定偏差,所以欧拉筛应运而生欧拉筛是一种线性复杂度的素数筛算法。原理基础是算术基本定理。基于算术基本定理,每一个数一定可以表示为若干个质数之积(质数除外)。所以每一个数的最小质因子是确定的,因此我们可以用最小质因子来标记每一个数,对于每一个数 i i i而言,对于每一个小于 i i i的最小质因子的质数 j j j都可以筛掉 i × j i\times j i×j这个数。且对于不同的 i i i而言满足 i × j i\times j i×j不重不漏。
举例:一开始枚举 2 2 2时将 2 2 2标记为质数并将其最小质因数定为 2 2 2,然后将 2 × 2 2\times2 2×2的最小质因数标记为 2 2 2,接下来枚举 3 3 3时先将小于 3 3 3 2 2 2与之相乘得到的 6 6 6的最小质因子标记为 2 2 2然后再算 3 × 3 3\times3 3×3的最小质因子。像这样每次将小于等于该数的最小质因子的数与这个数本身相乘,就能做到不重不漏。
具体看代码

code

int f[Size], q[Size];

void make_q(int n)
{
    int top = 0;
    for(int i = 2 ; i <= n ; i++)
    {
    	if(!f[i])
    	{
    	    f[i] = i;
    	    q[top++] = i;
    	}
    	for(int j = 0 ; p[j] <= f[i] && j < top ; j++)
    	{
    	    f[i * p[j]] = p[j];
    	    if(p[j] > n / i)//当所枚举到的质数与当前数的积大于n时退出。
    	    	break;
    	}
    }
}

复杂度 O ( n ) O(n) O(n)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值