定义
素数(质数):在大于 1 的自然数中,只有 1 和该数本身两个因数的数 素数(质数):在大于1的自然数中,只有1和该数本身两个因数的数 素数(质数):在大于1的自然数中,只有1和该数本身两个因数的数
判断方法
方法1
时间复杂度
O
(
n
)
时间复杂度O(n)
时间复杂度O(n)
定义法
定义法
定义法
bool is_prime(int n){
if(n <= 1) return false;
for(int i = 2; i <= n; i++) if(n % i == 0) return false;
return true;
}
方法2
时间复杂度 O ( n 2 ) 时间复杂度O(\sqrt{\frac{n}{2}}) 时间复杂度O(2n)
性质:对于合数 a ,一定存在一个最小素数 p ≤ a 使得 p 性质:对于合数a,一定存在一个最小素数p≤\sqrt{a}使得p 性质:对于合数a,一定存在一个最小素数p≤a使得p ∣ | ∣ a (定性理解) a(定性理解) a(定性理解)
定量证明(反证法)
定量证明(反证法)
定量证明(反证法)
不妨设
a
为合数,
p
是
a
的最小质因子,则
a
p
是
a
的另一个因子,且
a
p
<
p
不妨设a为合数,p是a的最小质因子,则\frac{a}{p}是a的另一个因子,且\frac{a}{p}<p
不妨设a为合数,p是a的最小质因子,则pa是a的另一个因子,且pa<p
假设
p
>
a
假设p>\sqrt{a}
假设p>a
但是,由于
p
是
a
的最小质因子,所以
a
p
不是质数,而是合数,那么
a
p
中必定存在一个质因子
q
(不妨设另一个因子为
r
)
但是,由于p是a的最小质因子,所以\frac{a}{p}不是质数,而是合数,那么\frac{a}{p}中必定存在一个质因子q(不妨设另一个因子为r)
但是,由于p是a的最小质因子,所以pa不是质数,而是合数,那么pa中必定存在一个质因子q(不妨设另一个因子为r)
假设
q
>
a
,那么
p
×
q
×
r
>
a
×
a
×
r
=
a
×
r
>
a
假设q>\sqrt{a},那么p×q×r>\sqrt{a}×\sqrt{a}×r=a×r>a
假设q>a,那么p×q×r>a×a×r=a×r>a
又因为
a
=
p
×
q
×
r
,假设矛盾
又因为a=p×q×r,假设矛盾
又因为a=p×q×r,假设矛盾
所以,
q
≤
a
,最小质因子
p
>
a
假设也矛盾
所以,q≤\sqrt{a},最小质因子p>\sqrt{a}假设也矛盾
所以,q≤a,最小质因子p>a假设也矛盾
所以,
p
≤
a
所以,p≤\sqrt{a}
所以,p≤a
原理:检验 [ 1 , n ] 区间里的数是否有约数,检验区间从 [ 1 , n ] 缩小到 [ 1 , n ] 原理:检验[1,\sqrt{n}]区间里的数是否有约数,检验区间从[1,n]缩小到[1,\sqrt{n}] 原理:检验[1,n]区间里的数是否有约数,检验区间从[1,n]缩小到[1,n]
bool is_prime(int n){
if(n <= 1) return false;
for(int i = 2; i * i <= n; i++)
if(n % i == 0) return false;
return true;
}
方法3
在方法
2
的基础上,只筛奇数
在方法2的基础上,只筛奇数
在方法2的基础上,只筛奇数
因为除
2
以外的偶数都是合数,直接跳过,只关心奇数即可
因为除2以外的偶数都是合数,直接跳过,只关心奇数即可
因为除2以外的偶数都是合数,直接跳过,只关心奇数即可
bool is_prime(int n){
if(n <= 1) return false;
for(int i = 2; i * i <= n; i++) if(n % 2 == 0 || n % i == 0) return false;
return true;
}
方法4
原理:所有大于 3 的素数都可以表示为 6 n ± 1 的形式 原理:所有大于3的素数都可以表示为6n±1的形式 原理:所有大于3的素数都可以表示为6n±1的形式
证明:
n
∈
N
,
n
可以用
6
n
、
6
n
+
1
、
6
n
−
1
、
6
n
+
2
、
6
n
−
2
、
6
n
+
3
、
6
n
−
3
表示
证明:n∈N,n可以用6n、6n+1、6n-1、6n+2、6n-2、6n+3、6n-3表示
证明:n∈N,n可以用6n、6n+1、6n−1、6n+2、6n−2、6n+3、6n−3表示
其中,
6
n
是
6
的倍数,不是素数
其中,6n是6的倍数,不是素数
其中,6n是6的倍数,不是素数
6
n
±
2
是偶数,只有
2
才是素数
6n±2是偶数,只有2才是素数
6n±2是偶数,只有2才是素数
6
n
±
3
是
3
的倍数,只有
3
才是是质数
6n±3是3的倍数,只有3才是是质数
6n±3是3的倍数,只有3才是是质数
所以,当
n
>
0
时,
6
n
±
1
是质数
所以,当n>0时,6n±1是质数
所以,当n>0时,6n±1是质数
证毕
.
证毕.
证毕.
bool is_prime(int n){
if(n <= 1) return false;
if(n <= 3) return true;
if(n % 2 == 0 || n % 3 == 0) return false;
for(int i = 5; i * i <= n; i += 6) if(n % i == 0 || n % (i + 2) == 0) return false;//i : 6n - 1; i + 2 : 6n + 1
return true;
}
方法5
埃拉托斯特尼(
E
r
a
t
o
s
t
h
e
n
e
s
)筛法
埃拉托斯特尼(Eratosthenes)筛法
埃拉托斯特尼(Eratosthenes)筛法
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, p = 0;//p:素数的个数
cin >> n;
bool is_prime[n + 5];//标记是否为素数
int prime[n + 5];//prime[p]:第p位素数
for(int i = 0; i <= n; i++) is_prime[i] = true;
is_prime[0] = is_prime[1] = false;
for(int i = 2; i <= n; i++){
if(is_prime[i] != 0){
prime[p++] = i;
for(int j = i + i; j <= n; j += i) is_prime[j] = false;
}
}
return 0;
}
优化
1
优化1
优化1
只筛奇数
只筛奇数
只筛奇数
把
2
的倍数筛掉,直接让
i
从
3
开始循环每次加
2
把2的倍数筛掉,直接让i从3开始循环每次加2
把2的倍数筛掉,直接让i从3开始循环每次加2
这样做内存需求减半,操作大约也减半
这样做内存需求减半,操作大约也减半
这样做内存需求减半,操作大约也减半
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, p = 1;//p:素数的个数
cin >> n;
bool is_prime[n + 5];//标记是否为素数
int prime[n + 5];//prime[p]:第p位素数
for(int i = 0; i <= n; i++) is_prime[i] = true;
is_prime[0] = is_prime[1] = false;
prime[1] = 2;
for(int i = 3; i <= n; i += 2){
if(i % 2 != 0 && is_prime[i] != 0){
prime[p++] = i;
for(int j = i + i; j <= n; j += i) is_prime[j] = false;
}
}
return 0;
}
优化
2
优化2
优化2
假设存在
x
<
i
2
,不妨设
x
=
a
×
b
(
a
<
b
)
假设存在x<i^2,不妨设x=a×b(a<b)
假设存在x<i2,不妨设x=a×b(a<b)
如果
a
,
b
>
i
,那么
a
×
b
>
i
2
,与假设
x
<
i
2
矛盾
如果a,b>i,那么a×b>i^2,与假设x<i^2矛盾
如果a,b>i,那么a×b>i2,与假设x<i2矛盾
因此,有
a
≤
i
因此,有a≤i
因此,有a≤i
这意味着当我们循环
f
o
r
到
i
之前,就早已经将这个数
x
筛过
这意味着当我们循环for到i之前,就早已经将这个数x筛过
这意味着当我们循环for到i之前,就早已经将这个数x筛过
所以我们优化从
i
2
开始标记
所以我们优化从i^2开始标记
所以我们优化从i2开始标记
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, p = 1;//p:素数的个数
cin >> n;
bool is_prime[n + 5];//标记是否为素数
int prime[n + 5];//prime[p]:第p位素数
for(int i = 0; i <= n; i++) is_prime[i] = true;
is_prime[0] = is_prime[1] = false;
prime[1] = 2;
for(int i = 3; i <= n; i += 2){
if(i % 2 != 0 && is_prime[i] != 0){
prime[p++] = i;
for(int j = i * i; j <= n; j += i) is_prime[j] = false;
}
}
return 0;
}
优化
3
优化3
优化3
vector
#include<bits/stdc++.h>
using namespace std;
int main(){
int n, p = 1;//p:素数的个数
cin >> n;
vector<int> prime;//prime[p]:第p位素数
vector<bool> is_prime(n + 5, true);//标记是否为素数
is_prime[0] = is_prime[1] = false;
prime.push_back(2);
for(int i = 3; i <= n; i += 2){
if(i % 2 != 0 && is_prime[i] != 0){
prime.push_back(i);
for(int j = i * i; j <= n; j += i) is_prime[j] = false;
}
}
return 0;
}
优化
4
优化4
优化4
bitset
⚠
⚠
⚠
b
i
t
s
e
t
的大小得是确定的
bitset的大小得是确定的
bitset的大小得是确定的
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1000000;
int main(){
int n, p = 1;//p:素数的个数
cin >> n;
vector<int> prime;//prime[p]:第p位素数
bitset<MAXN + 5> is_prime; //标记是否为素数
is_prime.set();//都标记为true
is_prime[0] = is_prime[1] = false;
prime.push_back(2);
for(int i = 3; i <= n; i += 2){
if(i % 2 != 0 && is_prime[i] != 0){
prime.push_back(i);
for(int j = i * i; j <= n; j += i) is_prime[j] = false;
}
}
return 0;
}
方法6
分块筛选
方法7
线性筛法
Euler 筛法
欧拉筛法
参考
https://blog.csdn.net/way_back/article/details/122760006
#include <iostream>
#include <vector>
#include <fstream>
using namespace std;
int main(){
vector<int> prime(10000, 1);
for(int i=2; i<100; i++){
if(prime[i]){
for(int j=i; i*j<10000; j++)
prime[i*j] = 0;
}
}
ifstream in("prime.txt");
for(int k; in>>k && k>1 && k<10000; )
cout << k << " is " << (prime[k] ? "":"not ") << "a prime." << endl;
return 0;
}
bool isPrime4(int n) {
bool yes = false;
int num[SIZE] = { 0 };
for (int i = 2; i < SIZE; i++) {
if (!num[i]) {
for (int j = i + i; j < SIZE; j += i) {
num[j] = 1;
}
}
}
if (!num[n]) {
yes = true;
}
return yes;
}
bool isPrime5(int n) {
bool yes = false;
int num[SIZE] = { 0 };
if (n == 2) {
yes = true;
}
else {
for (int i = 0; i < SIZE; i++) {
if (!num[i]) {
for (int j = (2 * i + 3) * (2 * i + 3); j < (2 * SIZE + 3); j += 2 * (2 * i + 3)) {
num[(j - 3) / 2] = 1;
}
}
}
}
if ((n - 3) % 2 == 0) {
if (!num[(n - 3) / 2]) {
yes = true;
}
}
return yes;
}