在做数论题过程中,素数出现的频率很高,在基础题和中档题甚至很多高难度的题里面都很常见,这篇博客就来对素数及其使用做一个小小的总结
首先,我们应该明确,素数的定义:
素数(prime number)又称质数,有无限个。素数定义为在大于1的自然数中,除了1和它本身以外不再有其他约数的数称为素数。
素数个数估计
既然素数有无限多个,那么n以内的素数有多少个呢?
可以通过如下方式来估计n以内的素数个数
素数个数估计:π(n)≈x/ln(n)
筛素数
知道了有这么多的素数,那我们如何得到素数呢?
直接一个一个遍历判断显然是不可取的,在竞赛中,我们一般采用打素数表的方法来解决这个问题,不知道哪位高人给打素数表起了筛素数这个名字,个人感觉十分贴切。筛法有很多,这里我只介绍一下时间效率最高的欧拉筛法,请看代码
const int MAXN = 10000000 + 10;
bool is_prime[MAXN];//素数筛,true表示该数为素数
int prime[MAXN];//素数表,这里面存了MAXN以内的所有素数
int tol;//素数个数统计
void PrimeTable(){
//初始化
memset(is_prime,true,sizeof(is_prime));
tol=0;
//从最小的素数2开始筛
for(int i=2;i//如果是素数就加入素数表中
if(is_prime[i]) prime[tol++]=i;
//进行筛素数操作
for(int j=0;jif(i*prime[j]>MAXN)break;
//把当前数字与素数表中的每个数字的乘积全部筛掉(这些因为有其他因子了,所以不可能是素数)
is_prime[i*prime[j]]=false;
//这里很关键,它保证了每一个合数只被筛一遍(大家可以在纸上模拟一下这个过程,就会有深刻的理解)
if(i%prime[j]==0)break;
}
}
}
大数据小区间筛素数个数
模拟筛法筛区间内素数个数,先打表,再根据表进行筛素数操作
typedef long long ll;
const int MAXN = 1000000+10;
bool is_prime[MAXN];//true的为合数,false的为素数
int prime[MAXN/10];//存素数
bool num[MAXN];
int tol;
void find_prime(){
memset(is_prime,false,sizeof(is_prime));
tol=0;
is_prime[1]=1;
for(int i=2; iif(!is_prime[i]){
prime[tol++]=i;
for(int j=i*2; j1;
}
}
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int T;
cin>>T;
find_prime();
for(int cas=1; cas<=T; cas++){
int a,b;
scanf("%d %d",&a,&b);
int n=b-a;
memset(num,false,sizeof(num));
for(int i=0;iint j=0;
if(a%prime[i]!=0)
j=prime[i]-a%prime[i];//找到第一个需要筛掉的数(j+a)%prime[i]==0
if(a<=prime[i])
j+=prime[i];//(j+a)/prime[i]==1,则(j+a)是素数,向下推一个
for(;j<=n;j+=prime[i])
num[j]=true;
}
int ans=0;
for(int j=0;j<=n;j++){
if(!num[j]) ans++;
}
if(a==1) ans--;
printf("Case %d: %d\n",cas,ans);
}
return 0;
}
素因数分解
这里有两种素因数分解方法
首先,第一种朴素分解方法,这种方法适用于数字比较小的时候的因数分解
有两种不同的实现方式,大家可以任选其一
int a[MAXN/10];//保存因子
int b[MAXN/10];//保存因子个数
int tol2;
void sbreak(ll n){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
tol2=0;
for(int i=0; prime[i]*prime[i]<=n&&i//此处的tol为筛出的素数总数{
if(n%prime[i]==0){
a[tol2]=prime[i];
while(n%prime[i]==0){
b[tol2]++;
n/=prime[i];
}
tol2++;
}
}
if(n!=1){
a[tol2]=n;
b[tol2++]=1;
}
}
const int M = 1000010;
int facs[M]; //n的所有质因子
int tot; //n的质因子个数
int kind; //质因子种数
struct Factor{
int val; //该质因子的值
int num; //该质因子的个数
}factor[M];
void solve(int n){
tot=0;
for(int i=2;i*i<=n;i+=2){
while(!(n%i))
n/=i,facs[tot++]=i;
if(i==2) --i;
}
if(n>1) facs[tot++]=n;
}
void collect(int n){
solve(n);
kind=0;
factor[kind].val=facs[0];
factor[kind].num=1;
kind++;
for(int i=1;iif(facs[i]!=facs[i-1]){
factor[kind].val=facs[i];
factor[kind].num=1;
kind++;
}
else{
factor[kind-1].num++;
}
}
}
第二种,Pollard_rho因数分解,这种方法适用于数字比较大时的因数分解
这种素数分解方法涉及MillerRabin素数测试的相关知识,大家可以到网上其他文章中了解一下相关知识,嫌麻烦也没关系,代码里有注释,大家也可以看代码直接理解
#include
#define LL long long
#define case 10//一般8~10就够了
using namespace std;
LL Get_rand(LL n){
return 2 + rand()%(n-2);
}
LL Mul(LL a,LL b,LL m){
LL ans = 0;
while(b){
if(b & 1){
ans = (ans + a) % m;
b--;
}
else{
a = (2 * a) % m;
b >>= 1;
}
}
return ans%m;
}
LL Quick_mod(LL a,LL n,LL m){
LL ans = 1;
while(n){
if(n & 1){
ans = Mul(ans, a, m);
}
a = Mul(a, a, m);
n >>= 1;
}
return ans;
}
bool Miller_Rabbin(LL n){
if (n==2)return true;
if (n<2||!(n&1))return false;
int t=0;
LL a,x,y,u=n-1;
while((u&1)==0) t++,u>>=1;
for(int i=0;i<case;i++){
a=rand()%(n-1)+1;
x=Quick_mod(a,u,n);
for(int j=0;jif (y==1&&x!=1&&x!=n-1)
return false;
///其中用到定理,如果对模n存在1的非平凡平方根,则n是合数。
///如果一个数x满足方程x^2≡1 (mod n),但x不等于对模n来说1的两个‘平凡’平方根:1或-1,则x是对模n来说1的非平凡平方根
x=y;
}
if (x!=1)///根据费马小定理,若n是素数,有a^(n-1)≡1(mod n).因此n不可能是素数
return false;
}
return true;
}
long long factor[1000];//质因数分解结果(刚返回时是无序的)
int tol;//质因数的个数。数组小标从0开始
long long gcd(long long a,long long b){
if(a==0)return 1;//???????
if(a<0) return gcd(-a,b);
while(b){
long long t=a%b;
a=b;
b=t;
}
return a;
}
long long Pollard_rho(long long x,long long c){
long long i=1,k=2;
long long x0=rand()%x;
long long y=x0;
while(1){
i++;
x0=(Mul(x0,x0,x)+c)%x;
long long d=gcd(y-x0,x);
if(d!=1&&d!=x) return d;
if(y==x0) return x;
if(i==k){y=x0;k+=k;}
}
}
//对n进行素因子分解
void findfac(long long n){
if(Miller_Rabbin(n))//素数{
factor[tol++]=n;
return;
}
long long p=n;
while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1);
findfac(p);
findfac(n/p);
}
int main(){
int t;
LL n;
srand(time(NULL));
scanf("%d",&t);
while(t--){
scanf("%lld",&n);
if(Miller_Rabbin(n))
cout<<"Prime"<else{
tol = 0;
findfac(n);
LL minn = 99999999999999999;
for(int i=0; icout<return 0;
}
求n的因数个数(是因数,不是素因数)
对n进行素因数分解,得到p1^a1+p2^a2+……+pi^ai
则其因数个数为:(a1+1)*(a2+1)*……+(ai+1)
常见的素数的应用就是这些,欢迎大家留言补充