zoukankan      html  css  js  c++  java
  • 筛素数

    整理一下筛素数的方法

    我在网上了解到两种筛素数的方法

    一种是  1/3n*判断  的时间复杂度 

    一种是的时间复杂度应该是比这个低

    先说一下第一种的思路

    首先:一个数如果他除以一个素数除不尽,那么他除以该素数的倍数也除不尽

    所以我们可以这么考虑

    如果一个数是二或三的倍数 那么它一定不是素数

    于是 对于  1 2 3 4 5 6 7 8 9 10 11 12……

    那么排除23的倍数

    剩下的是 1 5 7 11 ……

    对于六个数

    6*n   6*n+1   6*n+2   6*n + 3   6*n + 4   6*n + 5

    只需要检测 6*n+1 6*n+5是不是素数就可以了

    然后就是小的优化了

    判断一个数x是不是素数

    一种是枚举2~x-1  一种是2~sqrt(x) 都不是最优化的

    由于之前已经存储了一些素数  可以枚举这些素数来判断  因为如果一个数除以一个素数除不尽  那么除以该素数的倍数一定除不尽

     

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 using namespace std;
     6 
     7 const int maxn = 105;
     8 int is[maxn], prm[maxn];
     9 
    10 int get(int n) {
    11     int k = 0;
    12     prm[k++] = 2;
    13     prm[k++] = 3;
    14     int x = 4;
    15     for(int i = 5; i <= n; i += x) {
    16         x = 6 - x;
    17         bool flag = true;
    18         for(int j = 0; prm[j] * prm[j] <= i && j < k; j++) {
    19             if(i % prm[j] == 0) {
    20                 flag = false;
    21                 break;
    22             }
    23         }
    24         if(flag) {
    25             prm[k++] = i;
    26         }
    27     }
    28     return k;
    29 }
    30 
    31 int main() {
    32     int k = get(100);
    33     for(int i = 0; i < k; i++) {
    34         printf("%d ", prm[i]);
    35     } puts("");
    36 }
    View Code

     

     

     

     第二类有很多种优化

    先写一下最原始的代码

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 using namespace std;
     6 
     7 const int maxn = 105;
     8 int flag[maxn], prm[maxn];
     9 
    10 int get(int n) {
    11     int k = 0;
    12     memset(flag, 1, sizeof(flag));
    13     for(int i = 2; i <= n; i++) {
    14         if(flag[i]) {
    15             prm[k++] = i;
    16             for(int j = i; j <= n; j += i) {
    17                 flag[j] = 0;
    18             }
    19         }
    20     }
    21     return k;
    22 }
    23 
    24 int main() {
    25     int k = get(100);
    26     for(int i = 0; i < k; i++) {
    27         printf("%d ", prm[i]);
    28     } puts("");
    29 }
    View Code

    思路 是大一刚开学在杭电的一个题解上看到的

    首先 2是素数 那么  2的倍数4 6 8……就一定不是素数

    然后3是素数  那么 3的倍数也就一定不是素数

     

    但是这种方法有很大的缺点

    就是比如删除2的倍数的时候 会删到6 12 18等而删3的倍数的时候也会删到  于是造成了删除的冗余  所以该算法的执行效率一般

    而接下来的优化全部都是对于这个方向的优化

     

    第一中优化是来自一篇博客

    他的思路是这样的

    一个数如果不是素数那么他的因子一定有他小的素数

    那么对于每个数i只要把i之前的所有素数都乘以i那么  就不会错过下一个数i+1是不是合数

    这个方法应该能理解

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 using namespace std;
     6 
     7 const int maxn = 105;
     8 int flag[maxn], prm[maxn];
     9 
    10 int get(int n) {
    11     int k = 0;
    12     memset(flag, 1, sizeof(flag));
    13     for(int i = 2; i <= n; i++) {
    14         if(flag[i]) {
    15             prm[k++] = i;
    16         }
    17         for(int j = 0; j < k && prm[j] * i <= n; j++) {
    18             flag[prm[j] * i] = 0;
    19         }
    20     }
    21     return k;
    22 }
    23 
    24 int main() {
    25     int k = get(100);
    26     for(int i = 0; i < k; i++) {
    27         printf("%d ", prm[i]);
    28     } puts("");
    29 }
    View Code
    还有一个优化  明早起来写
     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 using namespace std;
     6 
     7 const int maxn = 105;
     8 int flag[maxn], prm[maxn];
     9 
    10 int get(int n) {
    11     int k = 0;
    12     memset(flag, 1, sizeof(flag));
    13     for(int i = 2; i <= n; i++) {
    14         if(flag[i]) {
    15             prm[k++] = i;
    16         }
    17         for(int j = 0; j < k && prm[j] * i <= n; j++) {
    18             flag[prm[j] * i] = 0;
    19             if(i % prm[j] == 0) 
    20                 break;
    21         }
    22     }
    23     return k;
    24 }
    25 
    26 int main() {
    27     int k = get(100);
    28     for(int i = 0; i < k; i++) {
    29         printf("%d ", prm[i]);
    30     } puts("");
    31 }
    View Code

     昨晚想了两件事  一件就是整理了一下筛素数的思路  一个是  我相信的一些东西  

    继续昨晚写的这个优化

    之前的优化是这样的  

    对于一个数i的因子一定比i小

    所以只要在其之前枚举所有素数可以了

    然后这就引出一个问题

    会不会有重复去掉的数字

    答案是肯定的

    比如12  你会在 6的时候去除6*2  又会在4的时候去除4 * 3

    所以会有很多去除的重复

    我们最理想的状态时每个合数都被它最小的素因子去除一次

    刚刚那个例子

    去除重复的原因是

    由于4的时候去去除4*2  4*3   但是2能被4整除   这就牵扯到一个问题

    就是 以后的所有的数   都可以写成2*2*x的形式   

    也就是说4已经不是后面那些数的最小因子了  所以直接跳出循环就可以了

    吉大有个更优化的筛素数模板:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 using namespace std;
     6 
     7 const int maxn = 10000005;
     8 
     9 bool is[maxn]; int prm[maxn];
    10 
    11 int get(int n) {
    12     int e = (int) (sqrt(0.0 + n) + 1);
    13     memset(is, 1, sizeof(is));
    14     int k = 0;
    15     prm[k++] = 2;
    16     is[0] = is[1] = 0;
    17     int i;
    18     for(i = 4; i < n; i += 2) is[i] = 0;
    19     for(i = 3; i < e;  i += 2) if(is[i]) {
    20         prm[k++] = i;
    21         for(int s = i * 2, j = i * i; j < n; j += s) {
    22             is[j] = 0;
    23         }
    24     }
    25     for(; i < n; i += 2) if(is[i]) prm[k++] = i;
    26     return k;
    27 }
    28 
    29 int main() {
    30     int n;
    31         int k = get(10000000);
    32         for(int i = 0; i < k; i++) {
    33             printf("%d ", prm[i]);
    34         } puts("");
    35     return 0;
    36 }
    研究中……

    等我研究透了再来写

    1209

    今天我又学到了一个非常nb的方法   简直叹为观止  

    在这里非常感谢这篇博客的作者  对我的启发很大

    http://blog.csdn.net/morewindows/article/details/7354571

    在这里  作者讲到了一个很厉害的 叫数组状态压缩  叹为观止

    作者是这么考虑的   

    我们知道一个整数的每一位进行位运算来对每个位进行赋一或者判断

    一个数组可以考虑成一个‘非常大的一个整数’  因为一个数组占据的也是一段连续的空间

    一个整形数据占据32个字节  但是我们用到的只是01  所以可以把一个整形压缩到1/32

    用的是|和&这两种运算

    代码:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <cmath>
     5 using namespace std;
     6 
     7 const int maxn = 105;
     8 
     9 int prm[maxn / 3 + 1];
    10 int is[maxn / 32 + 1];
    11 
    12 
    13 int get(int n) {
    14     memset(is, 0, sizeof(is));
    15     int k = 0;
    16     for(int i = 2; i <= n; i++) {
    17         if((is[i / 32] & ( 1 << (i % 32) ) ) == 0) {
    18             prm[k++] = i;
    19         }
    20         for(int j = 0; j < k && prm[j] * i <= n; j++) {
    21             int num = prm[j] * i;
    22             is[num / 32] |= (1 << ( num % 32) );
    23             if(i % prm[j] == 0) break;
    24         }
    25     }
    26     return k;
    27 }
    28 int main() {
    29     int k = get(maxn - 5);
    30     for(int i = 0; i < k; i++) {
    31         printf("%d ", prm[i]);
    32     } puts("");
    33     return 0;
    34 }
    View Code

    这两天一直整理素数这个博客了  

    现在总结一下学到的这几种方法

    1、筛素数最原始的方法就是对于每一个素数把其倍数全部删除  这个没什么好说的  缺点也很明显  就是有很多书都被删除了多次

    2、第一种优化是对于删除数字不是直接把其后面的数字全部删除   而是  对于数组i删除的是i与之前素数的乘积

    原因就是对于一个数i若其是个合数那么气必有一个比他小的素数的因数

    3这种方法是对于第二种的时间上的优化  只加了一小步  但是确是画龙点睛    如果一个数是之前一个素数的倍数  那么  不用再继续往下乘下去 直接break就可以了

    原因是  该数可以转化成其最小素因子成绩的形式

    4最后一种是对于第三种在空间上的优化  运用了状态压缩的思想

    思路也很简单  一个整形数据有32位  我们可以利用其中一位来完成真或者假的判断

    1 if i was a prime    j = i j <= n j+= i  is[j] = false

    2 i * pre prime[j]  = false

    3 if(i % prm[i] == 0) break

    4 is[x / 32] |= ( 1 << ( x % 32 ) )  if( ( is[x/32] & ( 1 << (x % 32) ) ) == 0)

  • 相关阅读:
    c++ 中pair类模板的用法详解
    求解Catalan数,(大数相乘,大数相除,大数相加)
    POJ--2823--Sliding Window----单调队列问题
    POJ2796 Feel Good -- 单调队列
    Graham扫描法 --求凸包
    山东理工大学第七届ACM校赛-G 飞花的传送门
    NKOJ1236 a^b (数论定理的应用)
    大数相减 C语言
    SPFA ----模板 O(kE) (k一般不超过2)
    C++大数相加
  • 原文地址:https://www.cnblogs.com/zhanzhao/p/4152194.html
Copyright © 2011-2022 走看看