从素数试除法到素数筛再到区间素数数量(模板)(优化思路)

1.素数概念:质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

  • 那么我们根据这个概念就可以提出我们最简单的判断素数的方法:试除法

2.试除法

  • 即:要判断这个数n是否为素数就可以让n对2 ~ n - 1逐个判断是否能被整除,如果被整除就说明这个数n不是素数。
    bool prime(int n)
    {
    	if(n == 1)
    	    return 0;
    	for(int i = 2;i <= n - 1;i++)
    	    if(n % i == 0)
    	        return 0;
    	return 1;	
    }

  • 优化1.0:当然我们可以发现我们从2判断到n - 1十分没有必要,因为最大可以被n整除的数一定比n / 2小,所以我们可以优化成这样:

    for(int i = 2;i <= n / 2;i++)
    	    if(n % i == 0)
    	        return 0;

  •  优化2.0:然而这样还没有到这种算法的极限,因为如果一个数不是质数那么n = a * b,那么一定有一个数大而另一个小,或者相等,所以我们无需从2判断到n - 1,只要从2判断到n^{\frac{1}{2}}即可:
    int nn = sqrt(n)	
    for(int i = 2;i <= nn;i++)
    	    if(n % i == 0)
    	        return 0;

  • 不过即使如此优化,这个算法在判断极多个数时的效率仍然很低,复杂度为O(mn^{1/2}),这时我们就要搬出素数筛算法了.

3.素数筛

  • 思路:素数的概念为除了1和它本身以外不再有其他因数,那么我们把所有因数的倍数全部排掉就能得到最后的素数了,而因数的基本单位就为素数,只需将素数的倍数排除就相当于对所有因数排除,而对于一个数来说因数不可能比它大,所以我们可以从前往后边取素数边排除,并能保证取出的肯定是素数
    1234567891011121314151617181920
    1234567891011121314151617181920
    1234567891011121314151617181920
    1234567891011121314151617181920
    1234567891011121314151617181920

  • 代码: 
    void pre_prime(int n)
    {
    	notp[1] = 1;
    	for(int i = 2;i <= n;i++)
    		if(!notp[i])
    		{
    			int p = i;
    			while((p += i) <= n)
    			notp[p] = 1;
    		}
    }

4.区间素数数量

  • 结合试除法优化的的思想与素数筛我们就可以解决区间素数数量的问题
  • 思路:判断n是不是素数我们只需要判断2~n^1/2能否整除n即可,素数筛又告诉我们只需要用素数来判断筛n所在的区间,如果n没被筛掉那么n就为素数.一结合,只要得到2~n^1/2的筛子再来筛begin~n的区间即可
  • 例题:洛谷P1835
  • 因为L,R很大但是R - L在一个合理的范围,因此我们可以将数组notprime[L~R]平移到notprime[0~R - L]
  • 注意1不是素数,需要特判
  • 代码:
    #include<bits/stdc++.h>
    using namespace std;
    int notp[70000] = {0};
    int notprime[1100000] = {0};
    typedef long long ll;
    int rr;
    void pre_prime(int n)
    {
    	notp[1] = 1;
    	for(int i = 2;i <= n;i++)
    		if(!notp[i])
    		{
    			int p = i;
    			while((p += i) <= n)
    			notp[p] = 1;
    		}
    }
    void pre_range_prime(ll l,ll r)
    {
    	for(int i = 2;i <= rr;i++)
    	{
    		if(!notp[i])
    		{
    			int p;
    			if(i >= l&&i <= r)//找前一个数
    			p = i;
    			else if(l % i == 0)
    			p = l - i;
    			else
    			p = l - l % i;
    			while((p += i) <= r)
    			notprime[p - l] = 1;
    		}
    	}
    }
    int main()
    {
    	int l,r;cin >> l >> r;
    	rr = sqrt(r);
        pre_prime(rr);
        pre_range_prime(l,r);
    	if(l == 1)
    	notprime[0] = 1;
    	int sum = 0;
    	for(int i = 0;i <= r - l;i++)
    	if(!notprime[i])
    	sum++;
    	cout << sum;
    }

 结束

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值