在做题时,数学方面的题目很多会对质数进行操作或者基于质数进行更多的操作。
一、质数判断
判断一个数是否为质数,我们通常使用的方法是从2开始到n看n是否能整除,因为质数的真因子只有1,所以当中间出现某个数可以被n整除时即说明n不是质数(特别注意1不是质数)。
// 判断n是否质数
bool f(int n)
{
for(int i = 2; i <= n; i ++ )
{
if(n % i == 0) return false; // 能够整除除1和n本身的数,不是质数
}
return ture; // 不存在整除情况,是质数
}
但在一些情况下,上面的方法所用时间过长并不能满足题目要求,因此我们继续进行分析。我们先来看个例子: 24的因子和25的因子。
24:2 * 12 = 24、3 * 8 = 24、4 * 6 = 24
也就是 :2、3、4、6、8、12
25:5 * 5 = 25
从上面的例子我们不难发现数 x 的因数一小一大成对,每对因数之中小的数最大不会超过sqrt(x)。因此循环限制条件可以有i <= x替换为 i * i < = x ;考虑i * i 可能会爆int, 所以限制条件等效换为 i <= x / i;
这样我们就可以写出下面问题的解决方法
问题介绍
给定n个整数,判断每个数是否质数。
输入格式
第一行包含整数n
接下来n行,每行包括一个正整数
输出格式
共n行,每行表示输入的正整数是否质数
是则输出“Yes”, 否则输出“No”
输入样例
2
2
6
输出样例
Yes
No
代码实现
#include <iostream>
using namespace std;
bool f(int x)
{
if(x == 1) return false; // 特判 1 不是质数
for(int i = 2; i <= x / i; i ++ )
{
if(x % i == 0) return false; // 不是质数
}
return true; // 是
}
int main()
{
int n; cin >> n;
while(n --)
{
int x; cin >> x;
if(f(x)) puts("Yes");
else puts("No");
}
}
二、分解质因数
每个合数都可以写成几个质数相乘的形式,所谓分解质因数就是把一个合数用质因数相乘的形式表示出来。因此分解质因数只针对合数。
想要代码实现我们需要先搞清楚如何分解的问题,所以来看以下过程:
24 / 2 = 12、12 / 2 = 6、6 / 2 = 3、3 / 3 = 1 --> 24 = 2^3 * 3^1
清楚上述过程之后代码实现的思路就有了,我们只需要通过循环来模拟这个过程
#include <iostream>
using namespace std;
void f(int x)
{
for(int i = 2; i <= x / i; i ++ ) // 底数从2开始
{
if(x % i == 0)
{
int s = 0; // s 表示相应质因数的指数
while(x % i == 0)
{
x /= i;
s ++;
}
cout << i << ' ' << s << endl;
}
}
if(x > 1) cout << x << ' ' << 1 << endl; // 未除尽的情况
puts("");
}
int main()
{
int n; cin >> n;
while(n -- )
{
int x; cin >> x;
f(x);
}
}
输入
2
6
8
输出
2 1
3 1
2 3
即6 = 2^1 * 3^1、8 = 2^3
三、筛质数
即筛选出1~n之间的素数。我们已经知道了如何判断素数,所以要筛选质数我们只需要从2(1不是质数)开始一直到n来分别判断每一个数。大体思路很简单,那么代码的重心就放在了效率上,也就是说,再n非常大的时候如何快速的进行筛选。
筛素数的方法有三种:朴素筛法、埃氏筛法、线性筛法(欧拉筛法)
据我所学埃氏筛法与朴素筛法相差不大,所以这里不再展示埃氏筛法。
朴素筛法 时间复杂度:O(n*logn)
朴素筛法的核心思想就是将查询到的质数的合数都删除。
#include <iostream>
using namespace std;
const int N = 1000010;
int n;
int p[N], cnt; // p用来存储质数, cnt用来记录素数的数量
bool st[N]; //标记是否素数
int f(int x)
{
for(int i = 2; i <= n; i ++ )
{
if(!st[i]) p[cnt ++ ] = i;
for(int j = i + i; j <= n; j += i) st[j] = true;
}
return cnt;
}
int main()
{
cin >> n;
cout << f(n) << endl;
return 0;
}
线性筛法(欧拉筛法) 时间复杂度:O(n)
朴素筛法在删除质数倍数时会出现重复删除的情况,所以欧拉筛法就是对此进行优化,规避重复删除的操作,提高代码执行效率。、
重点就在于if(i % p[j] == 0) break; 对于i*p[j]的操作。
#include <iostream>
using namespace std;
const int N = 1000010;
int n;
int p[N], cnt = 0;
bool st[N];
int f(int x)
{
st[1] = true;
for(int i = 2; i <= x; i++ )
{
if(!st[i]) p[cnt ++ ] = i;
for(int j = 0; i * p[j] <= x; j ++ )
{
st[i * p[j]] = true;
if(i % p[j] == 0) break;
}
}
return cnt;
}
int main()
{
cin >> n;
cout << f(n) << endl;
return 0;
}