素数的介绍
素数定义
质数(prime number)又称素数,有无限个。一个大于1的自然数,除了1和它本身外,不能被其他自然数整除,换句话说就是该数除了1和它本身以外不再有其他的因数;否则称为合数。
根据算术基本定理,每一个比1大的整数,要么本身是一个质数,要么可以写成一系列质数的乘积;而且如果不考虑这些质数在乘积中的顺序,那么写出来的形式是唯一的。最小的质数是2。
--------360百科
一.判断一个数是否是素数(prime):
(1)暴力筛选法
根据素数的定义,我们可以简单地想到:若要判断n是不是素数,我们可以直接写一个循环(i从2到n-1,进行n%i运算,即n能不能被i整除,如被整除即不是素数。若所有的i都不能整除,n即为素数)。
bool isprime(int n)
{
if (n == 0 || n == 1)
return false;
for (int i = 2; i < n; i++)
{
if (n % i == 0)
{
return false;
break;
}
}
return true;
}
时间复杂度:O(n)
这时间复杂度一看就不咋乐观,于是我们简单优化一下。
bool isprime(int n)
{
if (n == 0 || n == 1)
return false;
for (int i = 2; i <= (int)sqrt(n); i++)
{
if (n % i == 0)
{
return false;
break;
}
}
return true;
}
时间复杂度:O(sqrt(n))
优化原理:素数是因子为1和本身, 如果num不是素数,则还有其他因子,其中的因子,假如为a,b.其中必有一个大于sqrt(num) ,一个小于sqrt(num) 。所以必有一个小于或等于其平方根的因数,那么验证素数时就只需要验证到其平方根就可以了。即一个合数一定含有小于它平方根的质因子。
(2)除了2和3以外,2和3的倍数都不是素数
bool isprime(int n)
{
if (n == 0 || n == 1)
return false;
if (n == 2 || n == 3)
return true;
if (n % 2 == 0 || n % 3 == 0)
return false;
for (int i = 2; i <= (int)sqrt(n); i++)
{
if (n % i == 0)
{
return false;
break;
}
}
return true;
}
时间复杂度:O(sqrt(n)/2) .
(3)
首先看一个关于质数分布的规律:大于等于5的质数一定和6的倍数相邻。例如5和7,11和13,17和19等等;
证明:令x≥1,将大于等于5的自然数表示如下:
······ 6x-1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1 ······
可以看到,不在6的倍数两侧,即6x两侧的数为6x+2,6x+3,6x+4,由于2(3x+1),3(2x+1),2(3x+2),所以它们一定不是素数,再除去6x本身,显然,素数要出现只可能出现在6x的相邻两侧。这里有个题外话,关于孪生素数,有兴趣的道友可以再另行了解一下,由于与我们主题无关,暂且跳过。这里要注意的一点是,在6的倍数相邻两侧并不是一定就是质数。
根据以上规律,判断质数可以6个为单元快进,即将方法(2)循环中i++步长加大为6,加快判断速度为什么这样呢?
证明如下:
首先我们要知道,对于一个连续的3个数,是不是一定有一个模3余0,一个模3余1,一个模3余2
我们目前已经知道了要判断的数 n = 6x+1 或 6x-1
如果我们每次增1一个一个的循环则一定会遍历下面6种数
6i-1, 6i, 6i+1, 6i+2, 6i+3, 6i+4
1.假设可以被6i,6i+2, 6i+4整除,也就是可以写成2*(3i), 2*(3i+1), 2*(3i+2),那么n一定也可以被2整除,那么n一定是个偶数,但是很明显6x-1,6x+1是奇数
2.假设能被6i+3整除,即可以写成3*(2i+1),那么n至少能被3整除,因为对于一个连续的3个数,是不是一定有一个模3余0,一个模3余1,一个模3余2,而因为6x被3整除,所以6x+1,6x-1一定不会被3整除,所以不需要考虑
最终只剩下6i-1和6i+1,只需要判断这两个就行
bool isprime(int n)
{
if (n == 0 || n == 1)
return false;
if (n == 2 || n == 3 || n == 5)
return true;
if (n % 2 == 0 || n % 3 == 0)
return false;
for (int i = 5; i <= (int)sqrt(n); i += 6)
{
if (n % i == 0 || n % (i + 2) == 0)
{
return false;
}
}
return true;
}
时间复杂度:O(sqrt(n)/3) .
二、筛素数
1.欧拉筛(线性筛)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e8+10;
bool as[N+1];
int prime[N+1];
int k,n,t,cnt;
void isprime()
{
as[0]=1;
as[1]=1;
for(int i=2;i<=N;i++)
{
if(!as[i]) prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=N;j++)
{
as[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
int main()
{
isprime();
cin>>n;
while(n--)
{
scanf("%d",&t);
printf("%d\n",prime[t]);
}
}
对于欧拉筛(线性筛),每个数只会被他自己的最小质因数筛选一次,也叫线性筛。
i ∗ prime[j] 这个数的最小值质因数就是 prime[j]
2.埃式筛
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//ios::sync_with_stdio(false);
const int N = 1e6+10;
int as[N+1];//一个数组两用
int k,n,t,cnt;
void isprime()
{
as[0]=1;
as[1]=1;
for(int i=2;i<=N;i++)
{
if(!as[i])
{
for(int j=i*2;j<=N;j+=i)
{
as[j]=1;
}
}
}
for(int i=1;i<=N;i++)
{
if(!as[i]) as[++cnt]=i;
}
}
int main()
{
isprime();
cin>>n;
while(n--)
{
scanf("%d",&t);
printf("%d\n",as[t]);
}
}
三、6s内求 1-n 之间的素数个数(n≤1e11)
法1: 复杂度 n^(3/4)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 4e6+10;
const int NN = 1e6+10;
const int p = 1e9 + 7;
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
ll cnt,n,k;
ll f[N],g[N];
void ccprime()
{
ll i,j,m;
for(m=1;m*m<=n;++m) f[m]=n/m-1;
for(i=1;i<=m;++i) g[i]=i-1;
for(i=2;i<=m;++i)
{
if(g[i]==g[i-1]) continue;
for(j=1;j<=min(m-1,n/i/i);++j)
{
if(i*j<m) f[j]-=f[i*j]-g[i-1];
else f[j]-=g[n/i/j]-g[i-1];
}
for(j=m;j>=i*i;--j)g[j]-=g[j/i]-g[i-1];
}
}
int main()
{
IOS;
while(cin>>n)
{
ccprime();
cout<<f[1]<<endl;
}
}
法2:Meisell-Lehmer(复杂度 n^(2/3))
#include<cstdio>
#include<cmath>
using namespace std;
#define LL long long
const int N = 5e6 + 2;
bool np[N];
int prime[N], pi[N];
int getprime()
{
int cnt = 0;
np[0] = np[1] = true;
pi[0] = pi[1] = 0;
for(int i = 2; i < N; ++i)
{
if(!np[i]) prime[++cnt] = i;
pi[i] = cnt;
for(int j = 1; j <= cnt && i * prime[j] < N; ++j)
{
np[i * prime[j]] = true;
if(i % prime[j] == 0) break;
}
}
return cnt;
}
const int M = 7;
const int PM = 2 * 3 * 5 * 7 * 11 * 13 * 17;
int phi[PM + 1][M + 1], sz[M + 1];
void init()
{
getprime();
sz[0] = 1;
for(int i = 0; i <= PM; ++i) phi[i][0] = i;
for(int i = 1; i <= M; ++i)
{
sz[i] = prime[i] * sz[i - 1];
for(int j = 1; j <= PM; ++j) phi[j][i] = phi[j][i - 1] - phi[j / prime[i]][i - 1];
}
}
int sqrt2(LL x)
{
LL r = (LL)sqrt(x - 0.1);
while(r * r <= x) ++r;
return int(r - 1);
}
int sqrt3(LL x)
{
LL r = (LL)cbrt(x - 0.1);
while(r * r * r <= x) ++r;
return int(r - 1);
}
LL getphi(LL x, int s)
{
if(s == 0) return x;
if(s <= M) return phi[x % sz[s]][s] + (x / sz[s]) * phi[sz[s]][s];
if(x <= prime[s]*prime[s]) return pi[x] - s + 1;
if(x <= prime[s]*prime[s]*prime[s] && x < N)
{
int s2x = pi[sqrt2(x)];
LL ans = pi[x] - (s2x + s - 2) * (s2x - s + 1) / 2;
for(int i = s + 1; i <= s2x; ++i) ans += pi[x / prime[i]];
return ans;
}
return getphi(x, s - 1) - getphi(x / prime[s], s - 1);
}
LL getpi(LL x)
{
if(x < N) return pi[x];
LL ans = getphi(x, pi[sqrt3(x)]) + pi[sqrt3(x)] - 1;
for(int i = pi[sqrt3(x)] + 1, ed = pi[sqrt2(x)]; i <= ed; ++i) ans -= getpi(x / prime[i]) - i + 1;
return ans;
}
LL lehmer_pi(LL x)
{
if(x < N) return pi[x];
int a = (int)lehmer_pi(sqrt2(sqrt2(x)));
int b = (int)lehmer_pi(sqrt2(x));
int c = (int)lehmer_pi(sqrt3(x));
LL sum = getphi(x, a) +(LL)(b + a - 2) * (b - a + 1) / 2;
for (int i = a + 1; i <= b; i++)
{
LL w = x / prime[i];
sum -= lehmer_pi(w);
if (i > c) continue;
LL lim = lehmer_pi(sqrt2(w));
for (int j = i; j <= lim; j++) sum -= lehmer_pi(w / prime[j]) - (j - 1);
}
return sum;
}
int main()
{
init();
LL n;
while(~scanf("%lld",&n))
{
printf("%lld\n",lehmer_pi(n));
}
return 0;
}
四、判断大素数
Miller Rabin 判断一个大数是不是素数
Pollard-Rho 找出大数的最大质因子
P4718 【模板】Pollard's rho 算法 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<cstdlib>
#include<cstdio>
#include<ctime>
#define li inline
#define re register
#define ll __int128
#define abs(a) ((a)>0?(a):-(a))
namespace Miller_Rabin
{
const int Pcnt=12;
const ll p[Pcnt]={2,3,5,7,11,13,17,19,61,2333,4567,24251};
li ll pow(re ll a,re ll b,re ll p)
{
re ll ans=1;
for(;b;a=a*a%p,b>>=1)if(b&1)ans=ans*a%p;
return ans;
}
li bool check(re ll x,re ll p)
{
if(x%p==0||pow(p%x,x-1,x)^1)return true;
re ll t,k=x-1;
while((k^1)&1)
{
t=pow(p%x,k>>=1,x);
if(t^1&&t^x-1)return true;
if(!(t^x-1))return false;
}return false;
}
inline bool MR(re ll x)
{
if(x<2)return false;
for(re int i=0;i^Pcnt;++i)
{
if(!(x^p[i]))return true;
if(check(x,p[i]))return false;
}return true;
}
}
namespace Pollard_Rho
{
#define Rand(x) (1ll*rand()*rand()%(x)+1)
li ll gcd(const ll a,const ll b){return b?gcd(b,a%b):a;}
li ll mul(const re ll x,const re ll y,const re ll X)
{
ll k=(1.0L*x*y)/(1.0L*X)-1,t=x*y-k*X;
while(t<0)t+=X;return t;
}
li ll PR(const re ll x,const re ll y)
{
re int t=0,k=1;re ll v0=Rand(x-1),v=v0,d,s=1;
while(true)
{
v=(mul(v,v,x)+y)%x,s=mul(s,abs(v-v0),x);
if(!(v^v0)||!s)return x;
if(++t==k){if((d=gcd(s,x))^1)return d;v0=v,k<<=1;}
}
}
li void Resolve(re ll x,re ll&ans)
{
if(!(x^1)||x<=ans)return;
if(Miller_Rabin::MR(x)){if(ans<x)ans=x;return;}
re ll y=x;while((y=PR(x,Rand(x)))==x);while(!(x%y))x/=y;
Resolve(x,ans);Resolve(y,ans);
}
li long long check(ll x)
{
re ll ans=0;Resolve(x,ans);
return ans;
}
}
signed main()
{
srand(time(0));
re int t;
scanf("%d",&t);
long long re x,res;
for(;t;--t)
{
scanf("%lld",&x);
if((res=Pollard_Rho::check(x))^x)printf("%lld\n",res);
else printf("Prime\n");
}
}