C++学习之【使用位操做符求素数分析】

先放普通代码:ios

#include <iostream>
using namespace std;

void getPrime_1()
{
    const int MAXN = 100;
    bool flag[MAXN];
    int primes[MAXN / 3 + 1], pi=0;
    primes[pi++]=2; //2 是一个素数,先记录下来
    int i, j;
    for(i=0;i<MAXN;++i)flag[i]=false;//所有置假,没访问一个,相应位置真
    for (i = 3; i < MAXN; i+=2){//大于2的偶数必定不是素数,因此只要判断奇数便可
        if (!flag[i])//若是是素数
        {
            primes[pi++] = i;
            for (j = i; j < MAXN; j += i)//i的倍数必定都不是素数
                flag[j] = true;
        }
    }
    for(i=0;i<pi;++i)
        cout<<primes[i]<<" ";
    cout<<endl;
}

这里使用了素数表,每个bool型占用1个字节,共8位二进制位。并且这里除了多用了不少的无用bool变量在flag数组里,能够看到,咱们使用的flag数组只用到2号位以后的全部奇数位。所以,这里能够进行压缩改进一下:git

  1. flag数组减小一半
  2. 使用位操做符使空间占用减小为原来的八分之一

这里使用到对指定位置置1的操做:对于一个整数X能够经过将1左移n位后,与X进行操做,使X的第n置1github

int X=0;
int n=10;
X |= 1<< n; // 将 X 的第 n 位置 1

因此根据上面两条,优化后的代码以下:数组

void getPrime_2()
{    
    const int MAXN = 200;
    const int BitN=(MAXN/2)/32+1;//   
    int flag[BitN];
    int primes[MAXN / 3 + 1], pi=0;
    primes[pi++]=2;  //2 是一个素数,先记录下来
    int i, j; 
    for(i=0;i<BitN;++i)flag[i]=0;//全置0,每访问过一个,相应位置1
    for (i = 3; i < MAXN; i+=2){ //大于2的偶数必定不是素数,因此只要判断奇数便可
        if (!((flag[(i/2) / 32] >> ((i/2) % 32)) & 1))
        {
            primes[pi++] = i;
            //i的倍数必定都不是素数,其中,j加上一个i后为偶数,上一级已经不考虑了,因此还要加上一个i
            for (j = i; j < MAXN; j =j+i+i)
                flag[(j/2) / 32] |= (1 << ((j/2) % 32));
        }
    }
    for(i=0;i<pi;++i)
        cout<<primes[i]<<" ";
    cout<<endl;
}

首先,根据最大数,判断须要32的整型多少个:优化

BitN=(MAXN/2)/32+1;

MAXN/2去除了全部偶数位,(MAXN/2)/32+1表明须要多少32位的整型。
其次,全部求位的操做,都要除以2以去除偶数位的影响。
同时:spa

for (j = i; j < MAXN; j =j+i+i)
                flag[(j/2) / 32] |= (1 << ((j/2) % 32));

其中的 j=j+i+i; 是由于 ji 自己都是一个奇数,相加后为偶数,不考虑,因此还要加上一个 i
经过这种方式,缩小了占用空间。
代码文件code

MAXN=200 时,实际的素数只有45个,但primes的大小倒是66,那么这里怎么能更进一步的优化呢?blog

附:

因为这里使用的C++,那么为何不使用STL中已有的容器类bitset呢:get

#include <bitset>
void getPrime_3(){
    const int MAXN = 100;
    bitset<(MAXN/2+1)> flag(0); //不考虑偶数位
    int primes[MAXN / 3 + 1], pi=0;
    primes[pi++]=2;
    int i, j;
    for (i = 3; i < MAXN; i+=2){ //大于2的偶数必定不是素数,因此只要判断奇数便可
        if (!(flag.test(i/2)))//若是对应位为false
        {
            primes[pi++] = i;
            //i的倍数必定都不是素数,其中,j加上一个i后为偶数,上一级已经不考虑了,因此还要加上一个i
            for (j = i; j < MAXN; j =j+i+i)
                flag.set(j/2);//设置对应位为true
        }
    }
    for(i=0;i<pi;++i)
        cout<<primes[i]<<" ";
    cout<<endl;
}

能够看到,使用STL的代码仍是比较简洁的!it

下面去除重复计算的部分

刚才已经将偶数的计算去除了,但仍然还会有一部分的重复计算,好比:
i=3时,会访问15,同时,当i=5时,也会访问15
先上代码:

void getPrime_4()
{
    const int MAXN = 100;
    bitset<(MAXN/2+1)> flag(0); //不考虑偶数位
    int primes[MAXN / 3 + 1], pi=0;
    primes[pi++]=2;
    int i, j;
    for (i = 3; i < MAXN; i+=2)
    {
        if (!(flag.test(i/2)))
            primes[pi++] = i;
        for (j = 1; (j < pi)  && (i * primes[j] < MAXN); j++) // 1
        {
            flag.set(i*primes[j]/2);
            if (i % primes[j] == 0) // 2
                break;
        }
    }
    
    for(i=0;i<pi;++i)
        cout<<primes[i]<<" ";
    cout<<endl;
}

注释 1 表示让当前奇数和已经查出来的素数进行逐个相乘,相乘后的结果数确定不是素数!
注释 2 对于任何数来讲,若是它若是是该素数的倍数那么它就不能再与素数表中该素数以后的素数相乘了,如9是3的倍数,因此在9*3以后就不能再去用计算9*5了。
当数据量很大时,getPrime_4()getPrime_1()的差异将在时间和空间上是很大的!

若是阅读效果很差,请查看GitHub对应页面:

使用位操做符求素数

GitHubBlog /

相关文章
相关标签/搜索