zoukankan      html  css  js  c++  java
  • 求素数(从判断素数到筛法)

    判断素数

    • 最简单的判断就是根据素数的定义:只有两个因子1和本身(1不是素数)。时间复杂度O(n)
    bool is_prime(int x){
        if(x == 1) return false;
        rep(i , 2 , n-1){
            if(x % i == 0){
                return false;
            }
        }
        return true;
    }
    
    • 我们知道因子都是成对出现的,所以在枚举时,可以只枚举2-(sqrt x) .时间复杂度O((sqrt n))
    bool is_prime(int x){
        if(x == 1) return false;
        rep(i , 2 , sqrt(x)){
            if(x % i == 0){
                return false;
            }
        }
        return true;
    }
    
    • 数学上有个定理,只有形如6n-1,6n+1的自然数才可能是素数。简要说明一下:所有自然数都可以写成6n,6n+1,
      6n+2,6n+3,6n+4,6n+5.可以简单的根据偶数一定不是素数排除6n,6n+2,6n+4.而6n+3可以写成3(2n+1)是大于3的倍数也一定素数。所以可以进一步优化。
    bool is_prime(int x){
        if(x == 2 || x == 3) return true;
        if(x == 1 || (x%6!=1 &&x%6!=5)) return false;
        rep(i , 2 , sqrt(x)){
            if(x % i == 0){
                return false;
            }
        }
        return true;
    }
    

    埃氏筛法和欧拉筛法

    • 埃氏筛法思路:用已经筛选出来的素数去过滤所有能被它整除的数,即满足p|x的自然数x。我们知道2是最小的素数,通过2筛去所有2的倍数。然后往后
      3没有被筛,所以3是素数,通过3筛去所有3的倍数,以此类推。时间复杂度((nlog(log(n)))),虽然欧拉筛时间复杂度降到O(n),但埃氏筛法时间复杂度已经非常优秀,足够使用,并且埃氏筛法实现简单
      时间复杂度分析:小于(frac{n}{2}+frac{n}{3}+...+frac{n}{n} = nlogn).时间复杂度为O(nlog(log(n))).
    void Erasieve(int n){//筛选1-n间的素数
        rep(i , 1 , n) is_prime[i] = true ;//初始都为素数
        is_prime[1] = false; is_prime[2] = true ;
        rep(i , 2 , n){
            if(is_prime[i]){//判断是否为素数
                //prime[++len] = i ;//加入素数表
                for(int j = i*i ; j <= n ; j+=i){//通过该素数筛去素数倍数,j初始为i*i,而不是2*i,是因为我们知道2的倍数已经被筛了,算是一个小小的优化
                    is_prime[j] = 0;
                }
            }
        }
    }
    
    • 欧拉筛法:因为埃氏筛法可能对一个数进行多次筛除,比如30,会被2和5各筛除1次。而欧拉筛法是对埃氏筛法的优化
      使每一个数被筛除一次。要使每个数被筛除一次,则需要知道唯一分解定理。使每一个合数只被其最小质因数筛去。
      具体思路:对于任意一个自然数n,假设n的最小质因数为m,那么我们用所有小于m的质数(p_i)乘上n可以得到一个合数。对于这个合数而言,
      (p_i)一定是该合数最小质因数。因为n的最小质因数为m,(p_i)又为小于m的质数(可以看成是两部分相乘,一部分是大于m,这部分可能是质数也可能是合数,另一部分是(p_i)),
      所以(p_i)一定是该合数的最小质因数。
    void oulashai(int n){//筛选1-n素数
        ME(is_prime , true);
        is_prime[1] = false;
        for(int i = 2 ; i <= n ; i++){
            if(is_prime[i]){
                prime[++len] = i ;//素数表
            }
            for(int j = 1 ; j <= len && prime[j] * i <= n ; j++){//素数表保证p为素数
                is_prime[i*prime[j]] = false;//筛除n*p
                if(i % prime[j] == 0) break;//保证p是小于等于m的最小质因数
            }
        }
    }
    

    埃氏筛法

    应用一:区间无平方因子

    求[n,m]区间无平方因子数的个数。整数p无平方因子,当且仅当不存在k>1,使得p是(k^2)的倍数.((1<=n<=m<=10^{12} , m-n<=10^7))
    思路:首先筛选出不超过(sqrt{m})素数表,然后对素数的平方进行筛选。核心思想是埃氏筛法。

    int prime[maxn] , len;//素数表
    bool is_prime[maxn];//标记素数
    bool dprime[maxn];//标记具有平方因子数的数
    void solve(){
        int n , m , cnt = 0;
        cin >> n >> m;//[n,m]区间
        ME(is_prime , true);//初始化都为素数
        is_prime[1] = 0 ;//1不是素数
        for(int i = 2 ; i * i <= m ; i++){
            if(is_prime[i]){//通过素数筛掉素数倍数,剩下的就是素数
                prime[++len] = i ;
                for(int j = i * i ; j <= m ; j += i){//2*i已被素数2筛去。所有从i*i开始
                    is_prime[j] = 0 ;
                }
            }
        }
        for(int i = 1 ; i <= len ; i++){
            int p = prime[i]*prime[i];
            for(int j = p ; j <= m ; j += p){//类似埃氏筛法,筛掉含素数平方因子的数
                dprime[j] = 1 ;
            }
        }
        rep(i , n , m){
            if(!dprime[i]){//剩下的就是无平方因子的数
                //cout << i << endl;
                cnt++;
            }
        }
        cout << cnt << endl;
    }
    

    应用二:区间筛法

    给定整数l,r,求区间[l,r]的素数个数。(l<=r<=10^{12} , r-l<=10^6)
    解法:筛出(1-10^6)质数 , 因为小于(10^{12})的合数的最小质因子不超过(10^6)。可跳跃式的筛去较大的数。

    for(int i = 2 ; i <= 1000000 ; i++){
        if(!pr[i]){
            for(int j = i * i ; j <= 1000000 ; j+=i){
                pr[j] = 1 ;
                if(j >= l && j <= r){
                    vis[j-l] = 1 ;
                }
            }
            for(int j = max((ll)2 , (l+i-1)/i) ; j * i <= r ; j++){
                vis[j*i-l] = 1 ;
            }
        }
    }
    if(l == 1){
          vis[0] = 1 ;
    }
    vector<int>v;
    for(int i = 0 ; i <= r-l ; i++){
        if(!vis[i]){
            v.push_back(i+l);
        }
    }
    

    应用三:统计1-n每个数质因子个数

    运用了埃氏筛法的思想.

    void solve(int x){
        pr[1] = 0 ;
        for(int i = 2 ; i <= x ; i++){
            if(!pr[i])
            for(int j = i ; j <= x ; j *= i){
                for(int k = j ; k <= x ; k += j){
                    pr[k]++;
                }
            }
        }
    }
    
  • 相关阅读:
    商用 三色灯 显示屏 原理概述
    高速LVDS时序到底怎么看
    quartus qsys sdram IP 使用
    Avalon 总线 时序 介绍
    以太网 mac层传输 verilog 测试程序
    quartus15.1 下程程序 电脑蓝屏 解决方法
    vivado 波形保存以及arp
    quartus timequest 使用过程中的笔记
    Modelsim 仿真指令force用法
    开发笔记(一)Kintex
  • 原文地址:https://www.cnblogs.com/nonames/p/13045388.html
Copyright © 2011-2022 走看看