【数论】质数
质数:①大于1;②不包含除了1和自身的其他约数。
试除法求解一个数是否为质数:
1 bool isPrime(int n) 2 { 3 if(n == 1) 4 return true; 5 for(int i = 2;i < n;++i) 6 { 7 if(n % i == 0) 8 return false; 9 } 10 return true; 11 }
时间复杂度为O(N);
优化:考虑d|n,则(n/d)|n,即约数都是成对出现的,因此只需要枚举较小的一部分即可,设d≤(n/d),可得d≤sqrt(n),因此时间复杂度可以优化至O(n1/2)。
1 bool isPrime(int n) 2 { 3 if(n == 1) 4 return true; 5 for(int i = 2;i <= n/i;++i) 6 { 7 if(n % i == 0) 8 return false; 9 } 10 return true; 11 }
当然注意判断条件,要么写成i≤n/i,要么直接记录下sqrt(n)的值然后放入判断条件,不要写成i*i≤n,两个i相乘容易溢出,也不要写成i≤sqrt(n),每一次都重算一次sqrt反而导致优化本末倒置了。
分解质因数:
每一个合数都可以由若干个质数相乘得到,因此可以把一个合数分解成若干个质因数相乘的形式。
试除法:
思想是这样的,尝试枚举所有的质因数,然后每次枚举到就将n除以这个质因数即可,只要能够整除就继续整除,那么怎么枚举所有的质因数呢?其实只要从2开始到n-1,枚举所有的数就可以了。
问题是这样不会导致我们枚举到其他合数吗?我们重新回到最初时的定义,每个合数都可以由质数相乘得到,当我们枚举完前面的质数时,其实意味着后面的合数其实已经被筛掉了。
举个例子:
n = 60;
(i = 2) 60 % 2 == 0;60 / 2 = 30;
(i = 2) 30 % 2 == 0;30 / 2 = 15;
(i = 3) 15 % 3 == 0;15 / 3 = 5;
(i = 4) 5 % 4 != 0;
(i = 5) 5 % 5 == 0;5 / 5 = 1。
可以看到合数4在被枚举前,其实已经被它自己的质因数2给消去了,因此后续的合数是不会被枚举到的。
优化:
考虑到n中其实最多只包含一个大于n1/2的质因子,反证法即可证得,如果n中包含两个或以上大于n1/2的质因子,那么这两个质因子一相乘得到的结果就大于n了,就矛盾了。
因此可以先枚举到n1/2,然后再看看最后的值是否大于1,如果大于1,则说明该值就是那个大于n1/2的质因子,再统计一次就可以了。
1 void division(int x) 2 { 3 for(int i = 2;i <= x / i;++i) 4 { 5 int cnt = 0; 6 while(x % i == 0) 7 { 8 x /= i; 9 cnt++; 10 } 11 if(cnt) 12 cout << i << " " << cnt << endl; 13 } 14 if(x > 1) 15 cout << x << " " << 1 << endl; 16 cout << endl; 17 }
质数筛:
筛出n以内的质数的方法有两种,一种是埃拉托斯特尼筛法,简称埃氏筛,操作很简单,从2开始到n,逐步筛去所有未被筛过的数的倍数,不好表述,直接看代码。
1 #include <iostream> 2 using namespace std; 3 4 const int N = 1000009; 5 int prime[N],st[N]; 6 7 int main() 8 { 9 int n,cnt = 0;; 10 cin >> n; 11 for(int i = 2;i <= n;++i) 12 { 13 if(!st[i]) //如果当前数没有被筛过,那么它就是质数 14 prime[cnt++] = i; 15 for(int j = 2;j <= n/i;++j)//将当前质数的所有倍数都筛掉,注意筛掉的数不要超过n即可 16 st[i * j] = true; 17 } 18 cout << cnt << endl; 19 return 0; 20 }
另一种是线性筛,埃氏筛中其实存在多次重复筛选的过程,浪费时间,比如2能把6筛一次,3也会把6筛一次,因此对于一个合数,我们就是用它的最小质因数筛一次就可以了,因此线性筛的时间复杂度是线性的,即O(n)。
1 #include <iostream> 2 using namespace std; 3 4 const int N = 1000009; 5 int prime[N],st[N]; 6 7 int main() 8 { 9 int n,cnt = 0;; 10 cin >> n; 11 for(int i = 2;i <= n;++i) 12 { 13 if(!st[i]) 14 prime[cnt++] = i; 15 for(int j = 0;prime[j] <= n/i;++j) 16 { 17 st[i * prime[j]] = true; //筛去prime[j]乘上i形成的合数 18 if(i % prime[j] == 0) //当prime[j]是i的最小质因数,就可以break了,避免重复筛 19 break; 20 } 21 } 22 cout << cnt << endl; 23 return 0; 24 }

浙公网安备 33010602011771号