JoeyDDong.

JoeyDDong‘s Blog.

Writing code. Learning. Capturing life.

质数的基础知识

在大于 1 的整数中,如果只能被 1 和本身整除,就被称为质数,或素数。

1. 质数的判定——试除法

方法 1

是最笨的方法,复杂度为 O(n)O(n)。因为没有经过优化,不建议使用。

bool is_prime(int n){
// 如果 n < 2 则肯定不为质数
if( n < 2 ) return false;
for(int i = 2 ; i < n ; i++){
// 任何一个数 i 能整除 n,说明 n 不是质数
if( n % i == 0) return false;
}
return true;
}

方法 2:优化后

性质:如果 dd 能整除 nn , 那么 n/dn/d 也能整除 nn。(即 nn 的约数都是成对出现的,例如,2 能整除 12,6 也能整除 12 )。

所以我们可以只枚举一对中较小的那一个。那么只需要枚举验证 [2,n]\left [ 2,\sqrt{n} \right ] 之间的数即可。复杂度为 O(n)O(\sqrt{n})。

AcWing 866: 试除法判定质数

问题链接:https://www.acwing.com/problem/content/868/

题目:

给定 n 个正整数 aia_i ,判定每个数是否是质数。

数据范围:

1≤n≤1001 ≤ n ≤ 100

1≤ai≤231−11 ≤ a_i ≤ 2^{31}-1

解题思路:

// Problem: https://www.acwing.com/problem/content/868/
// 质数判定
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

int n, x;

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 solve() {
cin >> n;
while (n--) {
cin >> x;
if (is_prime(x))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}

int main() {
cin.tie(0);
ios_base::sync_with_stdio(false);
solve();
return 0;
}

注意:判断条件处最佳写法为: i <= n / i 。

不推荐写成 i <= sqrt(n) 因为每次都会计算一次 sqrt,非常的慢,影响执行速度;

不推荐写成 i * i <= n 因为当 i 非常大,逼近 int 的上限 2147483647 时,容易导致 i*i 的结果溢出变为一个负数,此时的程序时不稳定的。

2. 分解质因数——试除法

质因数:是指能够整除给定正整数的质数。

  • 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)

从小到大尝试 n 的所有的因数。 只需要枚举到 sqrt(n) 即可(因为 n 中最多只包含一个大于 n 的质因数)

时间复杂度最慢是 O(n)O(\sqrt{n} )

AcWing 867: 分解质因数

问题链接:https://www.acwing.com/problem/content/869/

题目:

给定nn个正整数 aia_i ,将每个数分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。

数据范围:

1≤n≤1001 ≤ n ≤ 100

2≤ai≤2×1092 ≤ a_i ≤ 2 \times 10^9

解题思路:

#include <iostream>
#include <algorithm>
using namespace std;
void divide(int x){
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
{
int s = 0;
while (x % i == 0) x /= i, s ++ ;
cout << i << ' ' << s << endl;
}
if (x > 1) cout << x << ' ' << 1 << endl;
cout << endl;
}
int main(){
int count;
cin >> count;
while(count--){
int n;
cin >> n;
divide(n);
}
return 0;
}

3. 质数筛选

参考:质数其实没有那么多。100 万以内,大概有 8 万个质数。

方法 1:

这种筛选方法的时间复杂度为:nlognnlogn

#include <iostream>
#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++ ){
// 如果这个数已经被筛过了,那么留下的数是一个质数,直接就存到 primes 数组中
if(!st[i]){
// 把这个质数存进 primes 数组中
primes[ cnt ++ ] = i ;
}
// j 从 2i 开始循环,查看i的倍数,给它赋值为true,标记他不是一个质数
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;
}

方法 2:埃氏筛法

质数定理:1 ~ n 中,有nln⁡n\frac{n}{\ln{n} } 个质数。

这个方法中的时间复杂度约为 nlog⁡log⁡nn\log \log n ,基本上和 O(n) 复杂度是一个级别的。

这个算法也称为 埃氏筛法。

#include <iostream>
#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 数组中
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;
}

方法 3:线性筛法

n 只会被最小质因子筛掉

每一个数只会被最小质因子筛一次,所以是线性的。

注:下面两个代码的 primes 数组声明方式不一样。一个用普通数组,一个用 vector。供参考

// 注释版本
#include <iostream>
#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 ++){
// 把当前质数和i的乘积筛掉
st[ primes[j] * i] = true; // 只用最小质因子来筛选
if( i % primes[j] == 0) break; // 当这句话发生的时候,说明primes[j]一定是i的最小质因子
// 1. i % primes[j] == 0,此时,primes[j]一定是i的最小质因子,即 primes[j] 一定是 primes[j] * i 的最小质因子
// 2. i % primes[j] != 0,primes[j] 一定小于i的所有质因子,即 primes[j] 一定是 primes[j] * i 的最小质因子
}
}
}

int main(){
int n;
cin >> n;
get_primes(n);
cout << cnt << endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int N = 1000010;
int cnt;
bool st[N];
vector<int> primes;

void get_primes(int n) {
for (int i = 2; i <= n; i++) {
if (!st[i])
primes.push_back(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 << primes.size() << endl;
}
Programming#Algorithm#Math
Back to articles ←