数论常用内容——素数
来源:互联网 发布:windows获取时间戳 编辑:程序博客网 时间:2024/06/05 13:36
在做数论题过程中,素数出现的频率很高,在基础题和中档题甚至很多高难度的题里面都很常见,这篇博客就来对素数及其使用做一个小小的总结
素数
首先,我们应该明确,素数的定义:
素数(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<MAXN;i++){ //如果是素数就加入素数表中 if(is_prime[i]) prime[tol++]=i; //进行筛素数操作 for(int j=0;j<tol;j++){ if(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; i<MAXN; i++){ if(!is_prime[i]){ prime[tol++]=i; for(int j=i*2; j<MAXN; j+=i) is_prime[j]=1; } }}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;i<tol&&prime[i]*prime[i]<=b;i++){ int 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; 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;i<tot;i++){ if(facs[i]!=facs[i-1]){ factor[kind].val=facs[i]; factor[kind].num=1; kind++; } else{ factor[kind-1].num++; } }}
第二种,Pollard_rho因数分解,这种方法适用于数字比较大时的因数分解
这种素数分解方法涉及MillerRabin素数测试的相关知识,大家可以到网上其他文章中了解一下相关知识,嫌麻烦也没关系,代码里有注释,大家也可以看代码直接理解
#include <ctime>#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;j<t;j++){ y=Mul(x,x,n); if (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"<<endl; else{ tol = 0; findfac(n); LL minn = 99999999999999999; for(int i=0; i<tol; i++) minn = min(minn,factor[i]); cout<<minn<<endl; } } return 0;}
求n的因数个数(是因数,不是素因数)
对n进行素因数分解,得到p1^a1+p2^a2+……+pi^ai则其因数个数为:(a1+1)*(a2+1)*……+(ai+1)
常见的素数的应用就是这些,欢迎大家留言补充
0 0
- 数论常用内容——素数
- 数论常用内容——反素数
- 数论常用内容——整除
- 数论常用内容——阶乘
- 数论常用内容——高斯消元
- 数论常用内容——数根
- 数论——素数
- 数论——素数算法
- 数论常用内容——矩阵快速幂
- 数论常用内容——欧拉函数
- 数论常用内容——中国剩余定理
- 数论———素数测试
- 数论概览——素数篇
- 数论——素数筛法
- 数论——【模板】线性筛素数
- 【数论】【素数】素数相关基础——基础操作
- 【数论】【素数】素数相关基础——整数分解
- 【数论】【素数】素数相关基础——获得素数与判别素数
- I Hate It(hdu 1754)线段树
- Azure SQLServer 添加Azure Active Directory管理员
- 聊聊三方支付对接那点事儿
- spring boot 整合redis对查询数据做缓存( 利用spring的AOP技术)
- MongoDB学习记录04-MongoDB聚合操作
- 数论常用内容——素数
- Problem F: 多少个最大值?
- 关于背景层与内部层继承的解决方案
- 用cmd命令行,adb连手机,查看手机歌曲,中文歌曲乱码解决办法
- C++中ifstream/ofstream/fstream浅谈
- String转换为其他数据类型
- 源码分析系列
- STM32 时钟配置
- 5G 关键技术 通信人家园转载