质数
试除法判断质数
因为一个数的因数都是成对出现的,比如 12 12 12 的因数是 2 , 6 2,6 2,6 和 3 , 4 3,4 3,4 等等,所以我们只要枚举较小的那个因数就可以了,设较小的因数为 d d d ,要求的数为 n n n 则 n d \frac{n}{d} dn 和 d d d 都能整除 n n n ,即 d ∣ n d|n d∣n 且 n d ∣ n \frac{n}{d}|n dn∣n
所以枚举时,只枚举 d ≤ n d d \le \frac{n}{d} d≤dn 的数
整理得 d ≤ n d \le \sqrt{n} d≤n
Code
bool is_prime(long long x) {
if (x < 2) return false;
for (long long i = 2; i <= x / i; i ++ )
if (x % i == 0)
return false;
return true;
}
分解质因数
试除法
从小到大枚举所有数(既枚举了质数,又枚举了合数为什么不会出问题?因为在枚举到这个合数之前,就意味着把 2 ∼ i − 1 2\sim i-1 2∼i−1 之间是质因子全部除干净了,则当前的 n n n 当中已经不含所有 2 ∼ i − 1 2\sim i-1 2∼i−1 之间的质因子了, i i i 中也不包含任何 2 ∼ i − 1 2\sim i-1 2∼i−1 之间的质因子,则 i i i 一定是质数)
注意到, n n n 中最多只包含一个大于 n \sqrt{n} n 质因子,所有可以先枚举完小于 n \sqrt{n} n 的质因子,单独处理那个大于 n \sqrt{n} n 的质因子,优化时间复杂度 O ( n ) → O ( l o g n ∼ n ) O(n)\to O(logn\sim\sqrt{n}) O(n)→O(logn∼n) (上界)
Code
给定 1 1 1 个正整数 a i a_i ai ,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。
void div(int n) {
for(int i = 2; i <= n / i; i++) {
if(n % i == 0) { //i 一定是质数
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("");
}
筛质数
https://www.acwing.com/problem/content/870/
朴素筛法
从前往后取质数,把所有数的倍数全部删掉,这样筛过之后,剩下的数就是质数了
证明
对于任何一个没有被筛掉的数 p p p 而言, 2 ∼ p − 1 2\sim p-1 2∼p−1 中的任何一个数都不是 p p p 的约数,所以 p p p 就是一个质数
Code
int prime[N], cnt;
bool st[N];
//筛从2->n 之间的质数
void get_prime(int n) {
for(int i = 2; i <= n; i++) {
if(!st[i]) { //如果一个数没有被筛过,那么它是质数
prime[cnt++] = n;
}
for(int j = i + i; j <= n; j += i) {
st[j] = true;
}
}
}
时间复杂度
O ( n 2 + n 3 + n 4 + . . . + n n ) = O ( n ( 1 2 + 1 3 + 1 4 + . . . + 1 n ) ) O(\frac{n}{2}+\frac{n}{3}+\frac{n}{4}+...+\frac{n}{n})=O(n(\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{n})) O(2n+3n+4n+...+nn)=O(n(21+31+41+...+n1))
注意到, 1 2 + 1 3 + 1 4 + . . . + 1 n \frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{n} 21+31+41+...+n1 为调和级数
1 2 + 1 3 + 1 4 + . . . + 1 n = l n ( n ) + c ( c = 0.57... ) \frac{1}{2}+\frac{1}{3}+\frac{1}{4}+...+\frac{1}{n}=ln(n)+c(c=0.57...) 21+31+41+...+n1=ln(n)+c(c=0.57...)
所以,时间复杂度为
O ( n l n ( n ) ) ≤ O ( n l o g ( n ) ) O(nln(n))\le O(nlog(n)) O(nln(n))≤O(nlog(n))
朴素筛法的优化,埃氏筛法
不用把所有数的倍数删掉,只要把质数的倍数删掉就可以了,当一个数不是质数时,就不需要筛掉它所有的倍数,所以只需要稍微更改一下上面的循环即可
void get_prime(int n) {
for(int i = 2; i <= n; i++) {
if(!st[i]) { //如果一个数没有被筛过,那么它是质数
prime[cnt++] = n;
for(int j = i + i; j <= n; j += i) st[j] = true;
}
}
}
时间复杂度
根据质数定理: 1 ∼ n 1\sim n 1∼n 中有 n l n ( n ) \frac{n}{ln(n)} ln(n)n 个质数,时间复杂度为 O ( n l o g ( l o g n ) ) → O ( n ) O(nlog(logn))\to O(n) O(nlog(logn))→O(n) (趋近于)
线性筛法
基本思路
把每个合数用它的质因子筛掉,但核心是 n n n 只会被它的最小质因子筛掉,每次筛掉当前质数和 i i i 的乘积,当 i m o d p r i m e s [ j ] = 0 i \mod{primes[j]}=0 imodprimes[j]=0 时 b r e a k break break
正确性证明
- 当 i m o d p r i m e s [ j ] = 0 i \mod{primes[j]}=0 imodprimes[j]=0 时, p r i m e s [ j ] primes[j] primes[j] 一定是 i i i 的最小质因子, p r i m e s [ j ] primes[j] primes[j] 一定是 p r i m e [ j ] × i prime[j]\times i prime[j]×i 的最小质因子 → \to → 只用最小质因子筛掉了某个数
- 当 i m o d p r i m e s [ j ] ≠ 0 i \mod{primes[j]}\ne0 imodprimes[j]=0 时,由于是从小到大枚举的所有质数,并且没有枚举到 i i i 的任何一个质因子,说明 p r i m e s [ j ] primes[j] primes[j] 一定小于 i i i 的所有质因子, p r i m e [ j ] prime[j] prime[j] 也一定是 p r i m e s [ j ] × i primes[j]\times i primes[j]×i 的最小质因子
- 任何一个合数都会被筛掉
对于一个合数 x x x 一定存在一个最小质因子,假设 p r i m e s [ j ] primes[j] primes[j] 是 x x x 的最小质因子,当 i i i 枚举到 x ÷ p r i m e s [ j ] x\div primes[j] x÷primes[j] ,一定在枚举到 x x x 之前,所有 x x x 一定会被筛掉,并且,每个数都只会被筛一次,所以它的时间复杂度是 O ( n ) O(n) O(n) 的
Code
void get_prime(int n) {
for(int i = 2; i <= n; i++) {
if(!st[i]) prime[cnt++] = i;
for(int j = 0; prime[j] <= n / i; j++) { //primes[j]*i<=n 只筛去小于n的质数
st[prime[j]*i] = true;
if(i % prime[j] == 0) break; //primes[j] 一定是 i 的最小质因子
}
}
}