Acwing-基础算法课笔记之数学知识(质数)
一、质数
1、概述
在大于1的整数中,如果只包含1和本身这两个约数,就被成为质数,或者叫素数。
二、试除法判定质数
1、过程模拟
判定的方法为试除法
(1)朴素版的试除法判定质数
时间复杂度为
O
(
n
)
O(n)
O(n)
代码如下:
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;
}
(2)优化版的试除法判定质数
例如:设 d 为被除数,n 为要被判定的数
如果 n%d=0,则 n%(n/d)=0 ,所以只需枚举 d
⩽
\leqslant
⩽ n/d,所以 d
⩽
\leqslant
⩽
n
\sqrt{n}
n。
时间复杂度为
O
(
n
)
O(\sqrt{n})
O(n)
代码如下:
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;
}
三、试除法分解质因数
代码模板:
void divide(int n) {
for (int i = 2; i <= n / i; i++) {
int s = 0;
if (n % i == 0) {
while (n % i == 0) {
n /= i;
s++;
}
printf("%d %d\n", i, s);
}
}
if (n > 1)printf("%d %d\n", n, 1);//只能被其本身整除
puts("");
}
四、筛质数
1、朴素筛法的过程模拟
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|
⇓
\Downarrow
⇓
把2的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把3的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把4的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把5的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把6的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把7的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把8的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把9的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把10的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把11的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
⇓
\Downarrow
⇓
把12的倍数删掉
2 | 3 | 4 \xcancel{4} 4 | 5 | 6 \xcancel{6} 6 | 7 | 8 \xcancel{8} 8 | 9 \xcancel{9} 9 | 10 \xcancel{10} 10 | 11 | 12 \xcancel{12} 12 |
---|
代码模板:
void get_prime(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i])p[cnt++] = n;//如果没有被筛过的条件
for (int j = i + i; j <= n; j += i)st[j] = true;//将i的倍数筛掉
}
printf("%d", cnt);
}
根据上述代码可知:当第一重循环到 i 时,那么第二重循环就会循环 n i \frac{n}{i} in 次,推理公式如下:
n 2 \frac{n}{2} 2n+ n 3 \frac{n}{3} 3n+ ⋯ \dotsb ⋯ ⋯ \dotsb ⋯+ n n \frac{n}{n} nn
= n n n ( ( ( 1 2 \frac{1}{2} 21+ 1 3 \frac{1}{3} 31+ ⋯ \dotsb ⋯ ⋯ \dotsb ⋯+ 1 n \frac{1}{n} n1 ) ) ) 调和级数 \textcolor{red}{调和级数} 调和级数
所以 lim n → ∞ \lim\limits_{n\rarr\infty} n→∞lim ( ( ( 1 2 \frac{1}{2} 21+ 1 3 \frac{1}{3} 31+ ⋯ \dotsb ⋯ ⋯ \dotsb ⋯+ 1 n \frac{1}{n} n1 ) ) )
=
ln
n
\ln n
lnn+
c
c
c
注意:
c
是一个欧拉常数,约等于
0.577
\textcolor{red}{注意:c是一个欧拉常数,约等于0.577}
注意:c是一个欧拉常数,约等于0.577
ln n \ln n lnn = log e n \log\nolimits_en logen
由于 n log e n n\log\nolimits_en nlogen < log 2 n \log\nolimits_2n log2n,所以时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)
2、埃氏筛法的过程模拟
质数定理: 1 1 1~ n n n 个数中,有 n l n n \frac{n}{lnn} lnnn个质数。
例如以下序列:
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|
p-1 | p |
当前数字 11 11 11 的位置设为 p p p,则前面从小到大的序列为 2 ∼ \sim ∼ p-1,所以只要在位置 2 ∼ \sim ∼ p-1中的数存在能够被位置 p 的数整除就将其筛掉。
因为 n n n ( ( ( 1 2 \frac{1}{2} 21+ 1 3 \frac{1}{3} 31+ ⋯ \dotsb ⋯ ⋯ \dotsb ⋯+ 1 n \frac{1}{n} n1 ) ) )= n ln n n\ln n nlnn,所以 n ln n ln n \frac{n\ln n}{\ln n} lnnnlnn大概会等于 O ( n ) O(n) O(n),但这是一个粗略的估计,是不对的,真实的时间复杂度为 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)(当调和级数只算质数项时)。
例如:
当
n
=
2
32
n=2^{32}
n=232 时,
l
o
g
n
=
32
logn=32
logn=32,则
l
o
g
l
o
g
n
=
5
loglogn=5
loglogn=5。
代码模板:
void get_prime(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i]) {
p[cnt++] = n;
for (int j = i + i; j <= n; j += i)st[j] = true;
}
}
printf("%d", cnt);
}
3、线性筛法的过程模拟
∙ \bullet ∙概述:在筛的时候,从小到大枚举所有的质数,当我们的质数大于 n / i n/i n/i 的时候就跳出循环,即 n % i n\%i n%i = = = 0 0 0时, n % n\% n% ( n / p r i m e s [ j ] ) (n/primes[j]) (n/primes[j]) = = = 0 0 0。
代码模板:
void get_prime(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i])p[cnt++] = i;
for (int j = 0; p[j] <= n / i; j++) {//从小到大枚举所有的质数
st[p[j] * i] = true;//标记不是质数的数
if (i % p[j] == 0)break;//当p[j]是i的最小质因子,且p[j]是p[j]*i的最小质因子时,退出循环
}
}
printf("%d", cnt);
}
五、总结
1、埃氏筛法
埃氏筛法: O ( n l o g l o g n ) O(nloglogn) O(nloglogn);
2、线性筛法
n只会被它的最小质因子筛掉,分情况讨论如下:
(1)当
i
%
p
[
j
]
=
0
i\%p[j]=0
i%p[j]=0 时,
p
[
j
]
p[j]
p[j] 一定是
i
i
i 的最小质因子,且
p
[
j
]
p[j]
p[j] 也一定是
p
[
j
]
∗
i
p[j]*i
p[j]∗i 的最小质因子;
(2)当
i
%
p
[
j
]
!
=
0
i\%p[j]!=0
i%p[j]!=0时,
p
[
j
]
p[j]
p[j] 一定是小于
i
i
i 的所有质因子,且
p
[
j
]
p[j]
p[j] 也一定是
p
[
j
]
∗
i
p[j]*i
p[j]∗i 的最小质因子。