zoukankan      html  css  js  c++  java
  • ACM数论之旅1---素数(万事开头难(>_<))

    前言:好多学ACM的人都在问我数论的知识(其实我本人分不清数学和数论有什么区别,反正以后有关数学的知识我都扔进数论分类里面好了)

    于是我就准备写一个长篇集,把我知道的数论知识和ACM模板都发上来(而且一旦模板有更新,我就直接在博客上改了,所以记得常来看看(。・ω・))

    废话说完了,直接进入正题ヾ(=^▽^=)ノ

    素数,又叫质数,定义是除了1和它本身以外不再有其他的因数

    我们通过这个定义,可以写如下程序判断一个数是不是质数

    1 bool prime(int x){//判断x是不是质数,是返回true,不是返回false 
    2     if(x <= 1) return false; 
    3     for(int i = 2; i < x; i ++){
    4         if(x % i == 0) return false;
    5     }
    6     return true;
    7 }

    这个程序的时间复杂度是O(n),有没有更快的方法,当然

    看这个

     1 bool prime(int x){//判断x是不是质数,是返回true,不是返回false 
     2     if(x <= 1) return false; 
     3     for(int i = 2; i <= sqrt(x + 0.5); i ++){//0.5是防止根号的精度误差 
     4         if(x % i == 0) return false;
     5     }
     6     return true;
     7 }
     8 //另一种方法,不需要根号 
     9 bool prime(int x){//判断x是不是质数,是返回true,不是返回false 
    10     if(x <= 1) return false; 
    11     for(int i = 2; i * i <= x; i ++){//用乘法避免根号的精度误差 
    12         if(x % i == 0) return false;
    13     }
    14     return true;
    15 }
    16 //根据题目不同,如果i*i会爆int,记得开longlong 

    这个复杂度是O(√n),速度快多了(#°Д°)

    根据题目不同,有可能你需要先预处理出1~N这N个数是否是素数

    如果用刚刚的方法,复杂度就是O(n√n)

     1 #include<cstdio>
     2 const int N = 100000 + 5;
     3 bool prime[N];
     4 bool is_prime(int x){
     5     if(x <= 1) return false; 
     6     for(int i = 2; i * i <= x; i ++){
     7         if(x % i == 0) return false;
     8     }
     9     return true;
    10 }
    11 void init(){
    12     for(int i = 0; i < N; i ++){
    13         prime[i] = is_prime(i);
    14     }
    15 }
    16 int main(){
    17     init();
    18 }

    如果n大一点,就太慢了(。・ω・)ノ゙

    介绍一种新方法,埃筛

    埃筛--------------埃拉托斯特尼筛法,或者叫埃氏筛法

    原理:如果找到一个质数,那么这个质数的倍数都不是质数

    比如2是质数,那么4,6,8,10,12...都不是质数

    然后看3是质数,那么6,9,12,15,18,21...都不是质数

    然后看4,4已经被2标记为合数了,所以跳过

    然后看5......这样一直筛下去

     1 #include<cstdio>
     2 const int N = 100000 + 5;
     3 bool prime[N];
     4 void init(){
     5     for(int i = 2; i < N; i ++) prime[i] = true;//先全部初始化为质数 
     6     for(int i = 2; i < N; i ++){
     7         if(prime[i]){//如果i是质数 
     8             for(int j = 2*i; j < N; j += i){//从i的两倍开始的所有倍数 
     9                 prime[j] = false; 
    10             }
    11         }
    12     }
    13 }
    14 int main(){
    15     init();
    16 }

    因为一些数字,比如6既被2的for循环经过又被3的for循环经过,所以复杂度不是O(n)

    这个复杂度经过专业人士检验,复杂度O(nloglogn)(学过高数的小朋友可以自己证明≖‿≖✧当然也可以去百度)

    知道原理后,我们再稍微优化一下就更快了

     1 #include<cstdio>
     2 const int N = 100000 + 5;
     3 bool prime[N];
     4 void init(){
     5     for(int i = 2; i < N; i ++) prime[i] = true;
     6     for(int i = 2; i*i < N; i ++){//判断改成i*i<N 
     7         if(prime[i]){
     8             for(int j = i*i; j < N; j += i){//从i*i开始就可以了 
     9                 prime[j] = false;  
    10             }
    11         }
    12     }
    13 }
    14 int main(){
    15     init();
    16 }

    好戏都是要留到最后的≖‿≖✧确实还有O(n)的做法

    这个算法名字叫线筛

     1 #include<cstdio>
     2 const int N = 100000 + 5;
     3 bool prime[N];//prime[i]表示i是不是质数 
     4 int p[N], tot;//p[N]用来存质数 
     5 void init(){
     6     for(int i = 2; i < N; i ++) prime[i] = true;//初始化为质数 
     7     for(int i = 2; i < N; i++){
     8         if(prime[i]) p[tot ++] = i;//把质数存起来 
     9         for(int j = 0; j < tot && i * p[j] < N; j++){
    10             prime[i * p[j]] = false;
    11             if(i % p[j] == 0) break;//保证每个合数被它最小的质因数筛去 
    12         }
    13     }    
    14 }
    15 int main(){
    16     init();
    17 }

    这个方法可以保证每个合数都被它最小的质因数筛去

    所以一个数只会经过一次

    时间复杂度为O(n)

    其实loglogn非常小,把埃筛看成线性也无妨,毕竟它比线筛好写

    基于埃筛的原理,我们可以用它干很多事

    比如预处理每个数的所有质因数

     1 #include<cstdio>
     2 #include<vector>
     3 using namespace std;
     4 const int N = 100000 + 5;
     5 vector<int > prime_factor[N];
     6 void init(){
     7     for(int i = 2; i < N; i ++){
     8         if(prime_factor[i].size() == 0){//如果i是质数 
     9             for(int j = i; j < N; j += i){
    10                 prime_factor[j].push_back(i); 
    11             }
    12         }
    13     }
    14 }
    15 int main(){
    16     init();
    17 }
    View Code

    比如预处理每个数的所有因数

     1 #include<cstdio>
     2 #include<vector>
     3 using namespace std;
     4 const int N = 100000 + 5;
     5 vector<int > factor[N];
     6 void init(){
     7     for(int i = 2; i < N; i ++){ 
     8         for(int j = i; j < N; j += i){
     9             factor[j].push_back(i); 
    10         }
    11     }
    12 }
    13 int main(){
    14     init();
    15 }
    View Code

    比如预处理每个数的质因数分解

     1 #include<cstdio>
     2 #include<vector>
     3 using namespace std;
     4 const int N = 100000 + 5;
     5 vector<int > prime_factor[N];
     6 void init(){
     7     int temp;
     8     for(int i = 2; i < N; i ++){
     9         if(prime_factor[i].size() == 0){
    10             for(int j = i; j < N; j += i){
    11                 temp = j;
    12                 while(temp % i == 0){
    13                     prime_factor[j].push_back(i);
    14                     temp /= i;
    15                 }  
    16             }
    17         }
    18     }
    19 }
    20 int main(){
    21     init();
    22 }
    View Code

    世界之大无奇不有(。-`ω´-)数论是个可怕的东西

  • 相关阅读:
    第一周作业
    第八周作业
    第七周作业
    第五周作业
    第四周作业
    第三周作业
    第二周作业(markdown版本)
    第一周作业
    第六次作业
    第五周作业
  • 原文地址:https://www.cnblogs.com/linyujun/p/5198832.html
Copyright © 2011-2022 走看看