判断质数是在luogu和oj中常见的问题,经过这么多次的时间超限的折磨学习了不少方法,今天打算总结一下。
在判断质数之前首先要了解什么是质数,这里引用一下百度百科的解释:“质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数”,从定义出发第一中判断质数的方法就诞生了。以下是代码:
int is_prime(int n)
{
if(n == 1)
return 0;
for(i = 2 ; i < n ; i++)
{
if(n % i == 0)
return 0;
}
return 1;
}
但是显而易见的就是,一旦n过大,或者需要判断的数太多,效率就是一个大问题,所以我们需要一个效率稍微高点方法,我们要去思考,如何避免去重复过多无用的循环,我不难发现,我们所需要判断的数n如果不是质数,那么他一定至少有两个除了他本身和1以为的因子,并且因子是成对出现的,而且一定有一个大一个小(或者两个相等),所以我们只需要判断小的内部分是否为n的因子就可以了,这样一份新的代码就出现了。
int is_prime(int n)
{
if(n == 1)
return 0;
if(n == 2)
return 1;
for(i = 2 ; i <= sqrt(n) ; i++)//sqrt函数需要引用头文件math.h;
{
if(n % i == 0)
return 0;
}
return 1;
}
当然我们是不会就此满足滴 ,这样代码只能跑过一下简单的题目,如果稍微坑爹点的数据你仍然使用这样的代码只能获得一个大大滴TLE,虽然我们自己没有什么优秀的方法,不过有很多优秀的前辈总结了很好的筛法,其中比较出色的就是由希腊数学家埃拉托斯特尼提出的埃拉托斯特尼筛法,简称埃氏筛或爱氏筛。埃氏筛法的核心思想就是是
要得到自然数n以内的全部素数,必须把不大于n 的所有素数的倍数剔除,剩下的就是素数。
知道原理那么就是如何用代码实现了。
#include<stdio.h>
#include<math.h>
#include<string.h>
bool prime[1000010];
void as_prime(int n)
{
for(int i = 2 ; i <= n ; i++)
{
if(prime[i])
{
for(int j = 2*i ; j <= n ; j+=i)
{
prime[j] = false;
}
}
}
}
int main()
{
int i = 0;
int n = 0;
scanf("%lld",&n);
memset(prime , true , sizeof(prime));
as_prime(n);
for(i = 2 ; i<= n ; i++)
{
if(prime[i])
printf("%d\n",i);
}
return 0;
}
当然埃氏筛法肯定也不是最优的了,我们仔细观察每次筛数的过程,一个合数往往会被他所有的因子都筛选一遍,这样就导致了我们重复了很多已经进行的工作 ,所以我们可以进一步优化我们的代码,让他更加完美,这就要引出新的筛法,也是最优的方法——欧拉筛法(线性筛)。欧拉筛法的算法核心就是让每个数只被他的最小质因子数筛去。
比如6,可以被2筛走也会被3筛走,所以我们要保证让6只被2筛走就不会存在再重复筛去6的过程。那么接下来就是代码实现欧拉的想法。
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
bool vis[100010];
int prime[100010];
void ol_judge(int n)
{
int cnt = 0;
for(int i = 2 ; i <= n ; i++)
{
if(vis[i])
prime[cnt++] = i;
for(int j = 0 ; j < cnt && i * prime[j] <= n ; j++)
{
vis[i * prime[j]] = false;
if(i % prime[j] == 0) // 欧拉筛法的核心思想,使数据不会被多次筛走;
break;
}
}
}
int main()
{
int n = 0;
scanf("%d",&n);
memset(vis , true , sizeof(vis));
ol_judge(n);
for(int i = 2 ; i <= n ; i++)
{
if(vis[i])
printf("%d\n",i);
}
return 0;
}
补 :分解质因数
#include <bits/stdc++.h>
using namespace std;
int prime[10000010];
void is_prime(int n)
{
for(int i = 2; i <= n; i++)
{
if(!prime[i])
{
prime[++prime[0]] = i;
}
for(int j = 1; j <= prime[0] && prime[j] <= n / i; j++)
{
prime[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
is_prime(10000000);
int n;
cin >> n;
int ans = 0;
for(int i = 1; i <= prime[0]; i++)
{
if(n % prime[i] == 0)
{
ans++;
while(n % prime[i])
{
n /= prime[i];
}
}
}
if(n > 1) ans++;
cout << ans << '\n';
return 0;
}