一般筛法求素数

一般筛法求素数

标签: PAT


下面是一个模板代码,在PAT考试中能够流畅默写会大大加速我们的编程速度,一般筛法虽然在速度上不如线性筛法,但是在PAT考试中,这一点性能可以忽略不计了.另外需要特别注意的是:

PAT打表解法有可能无法提交!
PAT打表解法有可能无法提交!
PAT打表解法有可能无法提交!

重要的事情说三遍!,一定要记住

在这样的情况下,熟练掌握筛法就是至关重要的了.

下面是没有优化的代码,

    bool prime [MAX];
    memset(prime,true,MAX);
    vector<long> v;
    for(int i = 2; i < MAX; i ++){
        if(prime[i] == true){
        v.push_back(i);
            for(int j = i ; j < MAX ; j += i){
                prime[j] = false;
            }
        }
    }

有必要作几点说明:

  • prime最好使用bool数组,bool虽然逻辑上是占用一个字节.但是实际上,在定义成一个bool数组后,编译器有可能会对这个数组做一些优化,使用一个字节的二进制位来代表true或者false.所以在一些数字比较大,对内存的限制比较紧张的情况下,使用bool数组可以有效的节省内存.
  • memset函数的使用,为了避免因为平台不一样导致奇怪的问题,这里统一引入memory.h头文件.
  • 为了后面输出等其他操作的方便,存放素数的容器这里统一使用vector.

下面是一些可以优化的地方

  • 内层循环中,j 从 i开始筛后面的数字,这样其实是完全不必要的.可以直接让j 从 i的平方开始往后筛.这样做的理由是i 到 i平方之间会路过的数字肯定已经被比i小的数字筛过了.例如:质数5如果按照常规方法,要经过5 , 10 , 15 , 20 , 25.将这些数字拆分就会发现依次是1 * 5,2 * 5,3 * 5,4 * 5,5 * 5,而1,2,3,45的乘积在之前肯定已经被筛过.所以可以直接让j = i * i开始内层循环.
  • 上面这种优化仍然是存在问题的.这会让j的定义必须变得特别大.因为当i = 10000的时候,j就必须要达到100000000才可以容纳,这回导致没有某种类型可以容纳它.所以我们让i的初始值定义为sqrt(double(MAX) + 0.5),对MAX四舍五入后取根号操作,就可以让筛法的范围正好覆盖MAX的范围.

即使这样优化仍然有需要注意的地方
就是将素数放入vector的操作的位置.因为检查prime[i]是否为true的操作现在只进行到根号MAX就停止了.所以原来地方的对V的操作无法访问所有为true的prime元素.所以在执行完筛法之后,我们需要再扫描一遍prime数组,将prime为true的元素单独放入.

典型例题

  • PAT 1013 数素数:利用素数个数定理x/ln(x)估算出达到目标的素数范围,使用筛法晒出素数表.即可求解,注意控制格式.
  • PAT 1027 素数对擦想:筛出固定范围的表后,遍历减去相邻的即可求得个数.

下面是最终代码

    bool prime [MAX];
    memset(prime,true,MAX);
    vector<long> v;
    int m = sqrt(double(MAX) + 0.5);
    for(int i = 2; i < m; i ++){
        if(prime[i] == true){
            for(int j = i * i ; j < MAX ; j += i){
                prime[j] = false;
            }
        }
    }
    for(long i = 2 ; i < MAX;i ++){
        if(prime[i])
            v.push_back(i);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值