质数(素数)和合数的定义 大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。 合数是指在大于1的整数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。 质数的判定:试除法 此处,给出常规代码写法
1 2 3 4 5 6 7 8 9 10 11 bool is_prime (int n) { if (n < 2 ) return false ; for (int i = 2 ; i <= n; i++) if (n % i == 0 ) return false ; return true ; }
上述代码时间复杂度是O(n)。可是还能优化
例如:n = 12;i = 2; 那么 (n / i) 一定能整除 n。
12试除法试到2的时候同时可以知道6也是一个约数。
所以每次枚举的时候,只需要枚举较小的那个约数就行。
也就是说,只要 i <= (n / i)
下面给出优化过后的代码
1 2 3 4 5 6 7 8 9 10 11 bool is_prime (int n) { if (n < 2 ) return false ; for (int i = 2 ; i <= n / i; i++) if (n % i == 0 ) return false ; return true ; }
上述代码时间复杂度是O(n1/2 )
主要修改了for循环里面的判断条件,没有使用sqrt函数
,也没有使用i * i <= n
,而是使用了i <= n / i
。
因为使用sqrt函数慢,i * i 有爆 int 的可能 分解质因数:试除法 合数可以分解质因数,质数不能分解质因数,因为质数只能等于1乘本身这种形式,而1不是质数。 算数基本定理
也叫 唯一分解定理
,主要的内容为:任何大于 1 的正整数都能 唯一 的分解为有限个质数的乘积。根据这一定理任何一个合数都可以被分解成几个质数相乘的形式。
质因数,给几个例子理解一下。
1没有质因子。 5只有1个质因子,5本身。(5是质数) 6的质因子是2和3。(6 = 2 × 3) 2、4、8、16等只有1个质因子:2。(2是质数,4 =2²,8 = 2³,如此类推) 10有2个质因子:2和5。(10 = 2 × 5) 如何计算质因数:短除法
代码模拟如上操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void divide (int n) { for (int i = 2 ; i <= n / i; i++){ if (n % i == 0 ){ int s = 0 ; while (n % i == 0 ){ n /= i; s++; } printf ("%d %d\n" , i, s); } } if (n > 1 ) printf ("%d %d\n" , n, 1 ); puts ("" ); }
需要注意的点:
假如 i 是一个合数,那么它一定可以分解成多个质因子相乘的形式,这多个质因子同时也是 n 的质因子且比 i 要小,而比 i 小的数在之前的循环过程中一定是被条件除完了的,所以 i 不可能是合数,只可能是质数。 n中最多只含有一个大于sqrt(n)的因子,证明通过反证法:如果有两个大于sqrt(n)的因子,那么相乘会大于n,矛盾。 质数筛 埃氏筛法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> #include <cstring> #include <algorithm> using namespace std;const int N = 1000010 ;int primes[N], cnt;bool st[N];void get_primes (int n) { for (int i = 2 ; i <= n; i++){ if (st[i]) continue ; primes[cnt ++] = i; for (int j = i + i; j <= n; j += i) st[j] = true ; } }int main () { int n; cin >> n; get_primes (n); cout << cnt << endl; return 0 ; }
时间复杂度:O(nlog(logn)) 线性筛法(欧拉筛):
线性筛法是对朴素筛法的进一步优化,埃式筛法的缺陷在于,对于同一个合数,可能被筛选多次。为了确保每一个合数只被筛选一次,我们用每个合数的最小质因子 来进行筛选
之所以被称为线性,是因为:1 ~ n 之内的任何一个合数一定会被筛掉,而且筛的时候只用最小质因子来筛,每一个数都只有一个最小质因子,所以每个数都只会被筛一次 ,因此线性筛法是线性的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> #include <cstring> #include <algorithm> using namespace std;const int N = 1000010 ;int primes[N], cnt;bool st[N];void get_primes (int n) { for (int i = 2 ; i <= n; i ++ ){ if (!st[i]) primes[cnt ++ ] = i; for (int j = 0 ; primes[j] <= n / i; j ++ ){ st[primes[j] * i] = true ; if (i % primes[j] == 0 ) break ; } } }int main () { int n; cin >> n; get_primes (n); cout << cnt << endl; return 0 ; }
时间复杂度:O(n) 1、因为primes中质数是递增的,所以如果i%primes[j]!=0代表i的最小质因数还没有找到,即prime[j]小于i的最小质因数。但并不妨碍,primes[j]是primes[j] * i的最小质因数。 2、如果当i%prime[j]==0时,代表i的最小质因数是prime[j],primes[j]是primes[j] * i的最小质因数。 3、综上所述达到了每个数仅筛一次的效果,时间复杂度O ( n ) .