一.质数
-
定义
大于1的整数中,只有1和它本身的约数的整数就叫做质数,也叫作素数。 -
质数的判定
(1)试探法,时间复杂度为O(n)
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int n;
cin >> n;
if (n < 2) return 0;
for (int i = 2; i <= n; i++)
if (n % i == 0){
cout << n << "不是质数" << "\n";
return 0;
}
cout << n << "是质数" << "\n";
return 0;
}
(2)试探法优化,时间复杂度O(sqrt(n))
推理:因为如果d是n的约数,那么n/d也是n的约数,所以判定条件范围可以缩小到d<=n/d
循环判定的条件不推荐写i<=sqrt(n)
原因:每次循环都得执行一次sqrt(n),而且sqrt这个函数本身也不快
循环判定的条件不推荐写ii <= n
原因:如果i定义的是int类型,假设判定的数n=2147483647,那么(i+1)(i+1)就会溢出,影响结果的判定
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int n;
cin >> n;
if (n < 2) return 0;
for (int i = 2; i <= n / i; i++)
if (n % i == 0){
cout << n << "不是质数" << "\n";
return 0;
}
cout << n << "是质数" << "\n";
return 0;
}
- 分解质因数
(1)试探法,时间复杂度O(n)
可能会有人觉得这样除的话可能会有合数,但是在n%i==0成立之前所有的i值都是不行的,不是n的约数,也就是2-i-1的数。算到第一个约数i的时候,这个i一定是质数。
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int n;
cin >> n;
if (n < 2) return 0;
for (int i = 2; i < n; i++){
if (n % i == 0){
int s = 0;
while(n % i == 0){
n /= i;
s++;
}
cout << i << " * " << s;
}
}
if (n > 1) cout << " * " << n << "\n";
return 0;
}
(2)试探法优化,时间复杂度O(sqrt(n))
优化后的时间复杂度虽然也是O(sqrt(n)),但是和质数判断的时间复杂度不一样。分解质因数最好可以达到O(logn),例如n是2^k。
优化一样是从判定条件去优化,和判断质数一样
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int n;
cin >> n;
if (n < 2) return 0;
for (int i = 2; i < n / i; i++){
if (n % i == 0){
int s = 0;
while(n % i == 0){
n /= i;
s++;
}
cout << i << " * " << s;
}
}
if (n > 1) cout << " * " << n << "\n";
return 0;
}
- 筛质数
(1)核心思想:删除整数的倍数
(2)朴素筛
把2~n的所有数的倍数都筛一遍,无论合数还是质数
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int q[maxn],cnt = 0;
bool st[maxn] = {false};
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int n;
cin >> n;
for (int i = 2; i <= n; i++){
if (!st[i]) q[cnt++] = i;
for (int j = i + i; j <= n; j+=i) st[j] = true;
}
for (int i = 0; q[i]; i++) cout << q[i] << " ";
return 0;
}
时间复杂度分析:
(3)埃氏筛(爱氏筛)
a.在筛选范围n以内,删除所有质数的倍数
b.埃氏筛是朴素筛的改进,代码差不多,无非就是把一个for循环提到了if里面来
c.质数定理:1-n中有n/lnn个质数
d.时间复杂度分析:由朴素筛时间复杂度可以推出埃氏筛的时间复杂度为O(log(logn))
e.由于是线性的筛选方法,所以时间复杂度是O(n)
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int q[maxn],cnt = 0;
bool st[maxn] = {false};
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int n;
cin >> n;
for (int i = 2; i <= n; i++){
if (!st[i]) {
q[cnt++] = i;
for (int j = i + i; j <= n; j+=i) st[j] = true;
}
}
for (int i = 0; q[i]; i++) cout << q[i] << " ";
return 0;
}
(4)欧拉筛(线性筛)
a.若n≈10^6,线性筛和埃氏筛的时间效率差不多,若 n≈10^7,线性筛会比埃氏筛快了大概一倍
b.原理:让每个合数只筛选一次,被它的最小质因子筛选
c.时间复杂度O(n)
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int q[maxn],cnt = 0;
bool st[maxn] = {false};
int main(){
ios::sync_with_stdio(false),cin.tie(0);
int n;
cin >> n;
for (int i = 2; i <= n; i++){
if (!st[i]) q[cnt++] = i;//q数组里面存放的是质数,按升序进行排序
for (int j = 0; q[j] <= n / i; j++){
st[q[j] * i] = true;//q[j]是质数,i只是充当倍数
//能break说明i是这个素数的倍数,q[j]是最小质因子
//既然是最小质因子就要退出,不然会导致后面的合数也被筛选,从而导致重复筛选
if (i % q[j] == 0) break;
}
}
for (int i = 0; q[i]; i++) cout << q[i] << " ";
return 0;
}