【数论】质数

质数:①大于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 }

 

posted @ 2021-10-30 20:09  Modest-Hamilton  阅读(181)  评论(0)    收藏  举报