一.判断质数
思路:任何一个正整数,他的因数一定是成对出现的,如果一个数 x 有除了1和他本身以外的的其他因数,那么一定是一个因数大于根号x, 另一个小于根号x,所以我们可以根据这个性质,来用试除法进行判断是不是质数。
时间复杂度:O(sqrt(n))
代码:
1 bool is_prime(int x) 2 { 3 if (x < 2) return false; 4 for (int i = 2; i <= x / i; i ++ ) 5 if (x % i == 0) 6 return false; 7 return true; 8 }
二.分解质因数
思路:任何正整数,都可以表示成 X = P1^a * P2^b * .... * Pn^m, 其中P1 P2 Pn都是质数,也就说在自然界中,质数是原子级别的,任何一个数都可以用质数表示,由此可以推出任何一个正整数最多只有一个大于sqrt(x)的质因数(这是一条性质)
时间复杂度:O(sqrt(n))
代码:
1 #include <iostream> 2 using namespace std; 3 4 int main(){ 5 int n; 6 cin >> n; 7 while(n --){ 8 int a; cin >> a; 9 //任何一个正整数最多只有一个大于根号a的质因数 10 for(int i = 2;i <= a / i;++i){ 11 if(a % i == 0){//i 一定是质数, 因为从2到i-1之间没有一个数是a的因数,a又是i的倍数,所以从2到i-1没有一个数是i的因数,所以i是质数 12 int s = 0;//i的指数 13 while(a % i == 0) a = a / i, ++s;//把i除干净 14 cout << i << " " << s << endl; 15 } 16 } 17 if(a > 1) cout << a << " " << 1 << endl;//因为可能有大于根号a的质因数 18 cout << endl; 19 } 20 return 0; 21 }
三.筛质数
思路:有三种方法,分别是朴素筛法,埃式筛法,线性筛法
朴素筛法:
从2到n枚举每个数 i , 然后把 i 的所有倍数都删掉,如果枚举到某个数p, p没有被删掉,说明从2到p-1之间没有p的因数,所以可以证明p一定是质数,这个算法的缺点是会重复删除,比如, 12,在2的时候删除一次,在3的时候也被删除,由此可见会出现冗余的情况。
时间复杂度:O(nlog(n))
代码:
1 int cnt; 2 int flag[N], prim[N]; 3 //朴素筛法 O(nlog(log(n))) 4 void get_prime1(int n){ 5 for(int i = 2;i <= n;++i){ 6 if(!flag[i])//i 没有删除 7 prim[++cnt] = i;//prim里存的是所有质数 8 //用来删除当前所有数的倍数 9 for(int j = i+i;j <= n;j += i) 10 flag[j] = true; 11 } 12 }
埃式筛法:
和朴素筛法不同的就是删除所有质数的倍数
时间复杂度: O(nlog(log(n)))
代码:
1 //埃式筛法 O(nlog(log(n))) 2 void get_prime2(int n){ 3 for(int i = 2;i <= n;++i){ 4 if(!flag[i]){//i 没有删除 5 prim[++cnt] = i;//prim里存的是所有质数 6 //用来删除当前所有质数的倍数 7 for(int j = i+i;j <= n;j += i) 8 flag[j] = true; 9 } 10 } 11 }
线性筛法:
每一个和数都有一个最小质因数,所以我们可以用每一个和数的最小质因数来删除这个和数,所以可知不会重复删除,但是代码不太好理解,背熟
时间复杂度:O(n)
代码:
1 //线性筛法On 2 void get_prime3(int n){ 3 for(int i = 2;i <= n;++i){ 4 if(!flag[i])prim[++cnt] = i; 5 for(int j = 1;prim[j] <= n/i;++j){ 6 flag[i*prim[j]] = true;//pj 一定是 i * pj的最小质因数, 7 if(i % prim[j] == 0)break; 8 } 9 } 10 }