素数筛选及优化

素数的埃式筛选法的思想:对于不超过N的每个正整数,删除2的倍数,3的倍数,4的倍数……N-1的倍数,当处理完所有数之后,还没有被删除的就是素数。

void GetPrime(int N)      //从0到N筛选素数
{
    memset(vis,0,sizeof(vis));
    for(int i=2;i<=N;i++)
    {
        for(int j=i*2;j<=N;j+=i)//对N以内的倍数处理
        {
            vis[j]=1;                 //置为合数
        }
    }
    vis[0]=vis[1]=1;
}

这里介绍一个唯一分解定理:任何一个大于1的自然数 N,如果N不为素数,那么N可以唯一分解成有限个素数的乘积。
优化:
由唯一分解定理可知合数一定是素数的倍数,所以就不用再对它们的倍数进行处理而重复操作了。
所以我们可以在第6行和第7行之间插入:
if(!vis[i])
对一个 i(i>2),i 的2~ i-1倍数之前已经处理,所以只需处理 i 的 i~∞ 的倍数的情况。如果i>sqrt(N),则i*i>N,那么处理的倍数就是没有意义的。故第4行可改为:
for(int i=2;i<=sqrt(N);i++)

想想可不可以再优化?
我们可以打打草稿看一下:
2的倍数:4 6 8 10 12 14 16 18 20……
3的倍数:6 9 12 15 18 ……
5的倍数:10 15 20……
可以发现其中还是有重复操作的情况,3的2倍,5的2倍都是2的倍数,5的三倍都是3的倍数,这些数在处理2的倍数,3的倍数时已经处理过,而我们只需考虑3的3倍,3的4倍……5的5倍,5的6倍……
因此我们可以把第7行代码改为:
for(int j=i*i;j<=N;j+=i)

再加一个数组prime存放素数
这样可以得到一个比较好的算法
int prime[N1];              //存放素数的数组
bool vis[N1];               //判断是否为素数
void GetPrime(int N)      //从0到N筛选素数
{
    int ptot=0;
    memset(vis,0,sizeof(vis));
    memset(prime,0,sizeof(prime));
    for(int i=2;i<=N;i++)              //为了得到素数数组,要放弃之前的小优化,i应该小于等于N
    {
        if(!vis[i])
        {
            prime[ptot++]=i;
            for(int j=i*i;j<=N;j+=i)
            {
                vis[j]=1;                 //置为合数
            }
        }
    }
    vis[0]=vis[1]=1;
}

还可不可以再优化?
是的,可以。
欧拉筛选可以做到O(n)的时间复杂度,因为给出的算法已经可以应付大多数的素数题目,这里就不再讨论了。这里给出一个网址,有兴趣的朋友可以研究一下http://www.cnblogs.com/zyf0163/p/4734985.html

其实还有一种厉(wu)害(lai)的方法:
事先写一个程序对一亿以内的素数打表,输出素数文本,然后把它们放在解题代码的数组里,到时直接查表,即使你不会什么算法,也可以这样做。





你可能感兴趣的:(数论,素数筛选,数论,CC++)