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