数论基础--质数

质数定义:只有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 }

 

posted on 2020-12-02 22:08  greenofyu  阅读(285)  评论(0编辑  收藏  举报