zoukankan      html  css  js  c++  java
  • 【数论】素数的判定与筛法

    素数的判定与筛法

        判定:很简单嘛!暴力大法参上!

    #include<iostream>
    #include<cmath>
    unsigned long int n,i,j,a,b;
    using namespace std;
    int main()
    {
        cin>>n;
        j=(int)sqrt(n);
        for(i=2;i<=j;i++)
            if(n%i==0) break;
        if(i>j&&n!=0&&n!=1) cout<<"Yes";
        else cout<<"No";
    }
    

         (不相信从来不刷水的我竟然做了这样的题……)

          这就是传说中的O(根号N)大暴力……

          那么还有个算法叫Miller-rabin……

    那么我们来介绍一下这是个什么东西:

          首先让我们了解这几个概念:

          费马小定理:对于素数p和任意整数a,有ap ≡ a(mod p)(同余)。反过来,满足ap ≡ a(mod p),p也几乎一定是素数。

      伪素数:如果n是一个正整数,如果存在和n互素的正整数a满足 an-1 ≡ 1(mod n),我们说n是基于a的伪素数。如果一个数是伪素数,那么它几乎肯定是素数。

      Miller-Rabin测试:不断选取不超过n-1的基b(s次),计算是否每次都有bn-1 ≡ 1(mod n),若每次都成立则n是素数,否则为合数。 

         设待测数为n,取一个比n小的正整数a,设n-1=d*2^r。若n是素数,则要么a^d mod n = 1,要么存在一个i,满足0≤i<r且a^(d*2^i )mod n = -1

        经过独立的t轮Miller-Rabin算法将一个合数误判为素数的可能性不大于 (1/4)t,(正确实现的算法不会误判素数为合 数),这个误判概率在基于Fermat定理的算法中是最好的。一轮Miller-Rabin算法的最坏情况时间复杂度为 (1+O(1))log2(n)( 以模n乘法为基本操作)。如果以单精度乘法操作作为时间复杂度的衡量,则一轮优化的Miller-Rabin算法的最 坏情况时间复杂度为O(log32 (n) 。从时间复杂度来看Miller- Rabin算法的性能是很好的。在实际应用中,Miller-Rabin 算 法的实际执行速度也很快。

         大家可以多次调用该函数,使得错误率降低。

    #include<stdio.h>
    #include<stdlib.h>
    #include<cmath>
    bool witness(__int64 a,__int64 n)
    {
        __int64 t,d,x;
        d=1;
        int i=ceil(log(n-1.0)/log(2.0)) - 1;
        for(;i>=0;i--)
        {
            x=d;  d=(d*d)%n;
            if(d==1 && x!=1 && x!=n-1) return true;
            if( ((n-1) & (1<<i)) > 0)
                d=(d*a)%n;
        }
        return d==1? false : true;
    }
    bool miller_rabin(__int64 n)
    {
        if(n==2)    return true;
        if(n==1 || ((n&1)==0))    return false;
        for(int i=0;i<50;i++){
            __int64 a=rand()*(n-2)/RAND_MAX +1;
            if(witness(a, n))    return false;
        }
        return true;
    }
    int main()
    {
        int n,cnt;
        __int64 a;
        while(scanf("%d",&n)!=EOF)
        {
            cnt=0;
            while(n--)
            {
                scanf("%I64d",&a);
                if(miller_rabin(a))
                    cnt++;
            }
            printf("%d
    ",cnt);
        }
        return 0;
    }
    

     

    筛法:筛法有3种时间复杂度,详细说应该有四种,最后一种貌似是打表……

                   1.O(N*根号N)

                   2.O(NlogN)

                   3.O(N)

                  

                   第一种方法:很明显的暴力啊……循环+判断

                   第二种方法:这是比较常用的一种筛法:埃氏筛(俗称垃圾筛),根本思想很简单:我们从1往N搜,假如我们遇到了一个没有被标记为不是素数的数,那么我们把它的倍数(>1)都标记为非素数……

                   

    for(int i=2;i<=1000000;i++)
    if(!FP[i])
    {
            PL[++e]=i;
            for(long long j=(long long)i*i;j<=1000000;j+=i) FP[j]=1;
    }
    

              第三种方法:线性筛,这个还是根据代码来解释吧……

                   第一行:应该没必要解释吧

                   第二行:从1到N枚举

                   第三行:同埃氏筛

                   第四行:枚举素数

                   第五行:如果乘上i大于n了,那么后面也肯定大于n,so break……

                   第六行:把prime[j]的i倍标记为不是素数

                   第七行:这个是代码的核心句,如果没有这句,那么它的时间复杂度与埃氏筛一样,但是我们来看一看12,它是在什么时候被枚举到的?第6次循环,也就是说,我们这次如果不加这条语句,那么有写情况是算重复了,那么这条鱼句就可以保证它不会重复。

                   1. 任何一个合数都可以表示成一个质数和一个数的乘积
                   2. 假设A是一个合数,且A = x * y,这里x也是一个合数,那么有:一个合数(x)与一个质数(y)的乘积可以表示成一个更大的合数(Z)与一个更小的质数(a)的乘积
                 例如: 如果i = 8; 那么由于i%2 == 0; 因此对于i=8就只需要检查primes[1]=2即可,因为对于大于primes[1]的质数,像3,有:8*3 = 2*4*3 = 12*2
                  也就是说24(8*3=24)并不需要在8时检查,在12时才检查

  • 相关阅读:
    网络编程
    并发编程-线程池
    并发编程-集合
    并发编程-AQS
    并发编程-CAS
    并发编程-volatile和synchronized的区别
    并发编程-synchronized
    并发编程-java内存模型
    JVM-分代垃圾回收器
    性能优化
  • 原文地址:https://www.cnblogs.com/wxjor/p/5935165.html
Copyright © 2011-2022 走看看