• 数论基础--质数


    质数定义:只有1和本身两个约数的数称为质数(或素数)

    1、试除法判断质数

    根据定义,对于某个数n,枚举2-n-1,看是否能够整除,以此判断是否为质数

    但是因为因子是成对出现的,所以只需要枚举到<=sqrt(n)即可

     1 //时间复杂度sqrt(n)
     2 #include<iostream>
     3 using namespace std;
     4 int n;
     5 bool isprime(int n){
     6     if(n<2){
     7         return false;
     8     }
     9     for(int i=2;i<=n/i;i++){
    10         if(n%i==0){
    11             return false;
    12         }
    13     }
    14     return true;
    15 }
    16 int main(void){
    17     cin>>n;
    18     for(int i=0;i<n;i++){
    19         int a;
    20         cin>>a;
    21         if(isprime(a)){
    22             cout<<"Yes"<<endl;
    23         }else{
    24             cout<<"No"<<endl;
    25         }
    26     }
    27     return 0;
    28 }

    2、分解质因数

    给定一个数n,要求输出质因数分解后的结果

    常规思路就是枚举2-n-1中每一个数,对每一个因子进行计数,时间复杂度为O(n)

    但是对于一个数,他最多只有一个大于sqrt(n)的因子,所以可以只枚举从2-sqrt(n)的数

     1 #include<iostream>
     2 using namespace std;
     3 void divide(int n){
     4     for(int i=2;i<=n/i;i++){
     5         if(n%i==0){
     6             int s=0;
     7             while(n%i==0){
     8                 n/=i,s++;
     9             }
    10             cout<<i<<" "<<s<<endl;
    11         }
    12     }
    13     if(n>1){
    14         cout<<n<<" "<<1<<endl;
    15     }
    16 }
    17 int main(void){
    18     int n;
    19     cin>>n;
    20     for(int i=0;i<n;i++){
    21         int a;
    22         cin>>a;
    23         divide(a);
    24         cout<<endl;
    25     }
    26     return 0;
    27 }

    3、素数筛

    筛法的意思就是可以求出从2到n的所有质数

    朴素筛法的思想是对于每一个数将其小于n的倍数都筛掉,这样留下来的数x就是在2--x-1中都没有因子的,也就是质数时间复杂度为O(nlogn)

    时间复杂度:n/2+n/3+n/4+...+n/n=n(1/2+1/3+...+1/n)

    因为1/2+1/3+...+1/n是调和级数,约等于ln n + c ,所以时间复杂度为nlog n 

     1 void get_primes(int n){
     2     for(int i=2;i<=n;i++){
     3         if(!st[i]){
     4             primes[cnt++]=i;
     5         }
     6         for(int j=i+i;j<=n;j+=i){
     7             st[j]=true;
     8         }
     9     }
    10 }

    根据唯一分解定理:每个大于1的自然数,要么本身就是质数,要么可以写为2个或以上的质数的积,而且这些质因子按大小排列之后,写法仅有一种方式。

    得出可以用质数将所有合数筛掉。

    时间复杂度为O(log log n)

    证明:https://leetcode-cn.com/problems/count-primes/solution/zhe-ge-da-gai-shi-wei-yi-yi-ge-zheng-ming-liao-shi/

     1 void get_primes(int n){
     2     for(int i=2;i<=n;i++){
     3         if(!st[i]){
     4             primes[cnt++]=i;
     5             for(int j=i+i;j<=n;j+=i){
     6                 st[j]=true;
     7             }
     8         }
     9     }
    10 }

    上述的埃式筛的O(log log n)的原因一个合数就是被筛了多次,如果能保证一个合数只被筛一次的话,时间复杂度就能到线性。

    这就是欧拉筛。

    如果去掉if(i%primes[j]==0) break;这一句话,那么就和埃式筛没有区别(只需要在内层循环加一个判断j<cnt就和埃式筛没有区别)。

    所以我们的出发点就可以是这一句话

    首先证明一个合数一定会被他的最小质因子筛掉

      情况1:假设i%primes[j]==0的话,因为j是从小到大枚举的,所以primes[j]就是i的最小质因子,同时也是primes[j]*i的最小质因子。

      情况2:假设i%primes[j]!=0的话,同样因为j是从小到大枚举的,所以primes[j]一定小于i的所有质因子,所以primes[j]也是primes[j]*i的最小质因子(更大的在i上)

    其次再证明一个合数只会被他的最小质因子筛掉一次。

    要证目标,只需要证明一个合数不会被除最小质因子之外的数筛掉。

    当i%primes[j]==0时,内层循环会break,意思就是primes[j+1]不是primes[j+1]*i的最小质因子

    因为primes[j]是i的最小质因子,而primes[j]<primes[j+1],所以primes[j]也是primes[j+1]*i的最小质因子。所以primes[j+1]*i应该被primes[j]筛掉

    又由唯一分解定理可得,一个合数一定存在一个最小质因子,所以对于一个合数x,i在枚举到x之前,一定会先枚举到x/primes[j],所以一定会被primes[j]筛掉。

    因此,一个合数一定会被他的最小质因子筛掉,所以欧拉筛是线性筛。

    1 void get_primes(int n){
    2     for(int i=2;i<=n;i++){
    3         if(!st[i]) primes[cnt++]=i;
    4         for(int j=0;primes[j]<=n/i;j++){
    5             st[primes[j]*i]=true;//被primes[j]筛掉
    6             if(i%primes[j]==0) break;
    7         }
    8     }
    9 }
  • 相关阅读:
    example_php文件上传处理
    andorid SQLite数据库创建文件
    andorid 手机外部储存
    手机界面
    andorid 数据储存、SharedPreferences存储和手机内部储存
    andorid 数据储存
    andorid 多线程handler用法
    andorid 配置器组件和提示消息
    andorid 网格视图GridView
    andorid 列表视图之SimpleAdapter
  • 原文地址:https://www.cnblogs.com/greenofyu/p/14076835.html
Copyright © 2020-2023  润新知