zoukankan      html  css  js  c++  java
  • 质数

    参考文献:《信息奥赛一本通》,《算法竞赛指南》

    简单概述?

    首先我们都知道,质数是一个正整数,无法被除了1和它自身之外的任何自然数整除,若不满足,则这个正整数为合数。

    算术基本定理:

    每一个大于1的正整数都可以唯一分解成有限个质数的乘积,如:

    5=1*5    6=2*3    18=2*2*3 .......

    质数分布定理:

    (说实话这个我还没搞懂,等我搞懂再来补充)

    质数判定:

    试除法

    若一个正整数A为合数,则必定存在一个能整除A的数T,其中2 <= T <= sqrt(A);

    证明:我们可以用·反证法· ,即假设以上的结论不成立,则必定存在T满足sqrt(A)+1 <= T <= A-1

       因为 A / T == K,且T整除A,则K也一定整除A,但是K一定在2 <= K <=sqrt(A)中,所以假设不成立,原命题成立。(sqrt 为开根号)

    根据这个定理我们可以发现,只要扫描从2到sqrt(A)间所有整数是否整除A,可以就为合数,否则为质数。

    代码如下(时间复杂度为 O(sqrt(N)) )

    bool is_prime(long n){
        for(long i=2;i<=sqrt(n);i++)
        if (n%i==0) return 0;
        return 1;
    }

     当然我们可以用“Miller-Robbin”,多次判定出错率接近0,在这里就不介绍了。

    质数的筛选

    No.1 Eratosthenes 筛法 (简称埃氏筛)

    它的思想是,只要是质数的倍数就一定不是质数,    因为质数的倍数它一定可以表示成某个质数*倍数形式,不满足质数的定义。

    操作:从小到大枚举每一个质数,但只要是质数的倍数就全部标记成非质数,整数1特殊处理。

    例如过程如下:

    2 3 4 5 6 7 8 9 10 11 12 ...

    2 4 5 6 7 9 10 11 12...

    2 3 4  6 7 8  9 10 11 12...

    2 3 4 5 6  8  9  10 11 12...

    2 3 4 5 6 7 8  9  10  11  12...

    但是事实上6 会被2 和 3同时标记为合数,则说明小于x2(x的平方)的x的倍数在之前扫描更小的数时就已经标记过了,所以我们可以优化这个筛法,

    对于每一个x,把 >= x2的x的倍数标记为合数就可以了,即扫描x时从x2开始标记就行。

    算法复杂度为O(n logn logn),接近线性

    bool v[MAXN];  //合数标记
    void primes(long n){
    memset(v,0,sizeof v);
    for(long i=2;i<=n;i++){
        if(v[i]) continue;
        cout<<i<<endl; //i是质数
        for(long j=i;j<=n/i;j++) v[i*j]=1; //累计倍数
      }
    }

    No.2 线性筛法

    即使是优化后的Eratosthenes算法,它还是会有重复标记发生,例如12 还是会被2,3重复标记,它时间复杂度较高的根源是它无法唯一确定某个合数的产生方式。

    而线性筛法是通过“从大到小累积质因子”的方法去标记每个合数,因为每个合数只会被它的最小质因子筛一次,时间复杂度为O(N)。原理是算术基本定理

    代码如下

    long v[MAXN],prime[MAXN];
    void primes(long n){
        memset(v,0,sizeof v); //v表示最小质因子
        long m=0;//质数数量
        for(long i=2;i<=n;i++){
        if(v[i]==0){//i为质数
            v[i]=i;
            prime[++m]=i;
            }    
      //给i乘上一个质因子
        for(long j=1;j<=m;j++){
        if(prime[j]>v[i] || i*prime[j]>n) break;//若这个质数大于i的最小质因子,或者超出n的范围,就终止循环
        v[prime[j]*i]=prime[j];  //这样就可以得到prime[j]必定为prime[j]*i的最小质因子 (prime[j]<=v[i])
            }    
        }
        for(long i=1;i<=m;i++) printf("%ld
    ",prime[i]);
    }

     质因数分解

    它要根据算数基本定理来分解

    任何一个大于1的正整数都能唯一分解为有限个质数的乘积(ai为正整数,pi为质数,p1<p2<......<pn)

    方法:试除法

    我们可以扫描从2~sqrt(N)的每个数k,并判断k是否整除N,若成立,则继续除下去直到N无法被k整除 ,即除去N中所有的因子并记录除去的k值个数
    这样的话,N的合数因子一定在扫描到它之前就被除去了,因为根据算数基本定理,这个合数因子也可以被分解质因数,而它的质因数也是原合数的质因数,
    所以在之前就必定被抹杀了......
    当然如果N无法被2~sqrt(N)整除,则说明N是质数,应注意不要漏了。
    时间复杂度为O( sqrt(N) )
    void divide(long n){
        long m=0;    //质数数量
        for(long i=2;i<=sqrt(n);i++){
        if(n%i==0){  //若成立,则i必定为质数
        prime[++m]=i;
        a[m]=0;
        while(n%i==0) n/=i,a[m]++;
            }    
        if(n>1) //说明n本身为质数,无需分解
        p[++m]=n;a[m]=1;
        }
        for(long i=1;i<=m;i++)
        printf("%ld^%ld
    ",p[i],a[i]);
    }
    就这样吧......
    比较菜,若有问题望及时提出谢谢。
  • 相关阅读:
    【C++】链表回环检测
    【C++】满二叉树问题
    【C++】约瑟夫环(数组+链表)
    【C++】子序列匹配问题
    【OJ】抓牛问题
    【C++】基于邻接矩阵的图的深度优先遍历(DFS)和广度优先遍历(BFS)
    【C++】二叉树的构建、前序遍历、中序遍历
    范进中Nature——儒林外史新义
    VMware Workstation下ubuntu虚拟机无法上网连不上网络解决
    儒林外史人物——娄三、娄四公子
  • 原文地址:https://www.cnblogs.com/yuzhe123/p/13327001.html
Copyright © 2011-2022 走看看