素数
标题一
定义
质数(prime number)又称素数,有无限个。质数定义为在大于1的自然数中,除了1和它本身以外不再有其他因数。
一、素数的判定
试除法:
定理:若一个正整数是合数,则存在一个能整除N的数K,2≤K≤sqrt(n)
证明:反证法
Eratosthens筛选法
基本思想:质数的倍数一定不是质数
时间复杂度: O(n㏒㏒n)
欧拉线性筛
- 由于每个大于等于2的合数必定存在一个最小的质因数,所以只要筛去每个质数的倍数就相当于筛去了所有合数。
- 但欧拉筛相比埃氏筛最大的优化就在于欧拉筛保证每个合数只被筛了一次,且是被其最小的质因数筛去的,所以欧拉筛的时间复杂度可以达到O(N)。
-
代码
#include<stdio.h> #include<stdlib.h> #define FORa(i,s,e) for(int i=s;i<=e;i++) #define FORs(i,s,e) for(int i=s;i>=e;i--) #define gc pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),pa==pb)?EOF:*pa++ #define File(name) freopen(name".in","r",stdin);freopen(name".out","w",stdout); using namespace std; static char buf[100000],*pa=buf,*pb=buf; inline int read(); const int N=1000; int n,cnt,v[N+1],prime[N+1]; int main() { scanf("%d",&n); /*优化空间的欧拉质数筛 FORa(i,2,n) { if(!bz[i]) prime[++cnt]=i; FORa(j,1,cnt) { if(i*prime[j]>n) break; bz[i*prime[j]]=true; if(i%prime[j]==0) break; } }*/ FORa(i,2,n) { if(!v[i]) v[i]=i,prime[++cnt]=i; FORa(j,1,cnt) { if(prime[j]>v[i]||prime[j]*i>n) break; /*因为整个程序会经过整个队列,所以可以保证都会被筛 prime[j]>v[i]是为了prime[j]*i不被再次被筛 如果 prime[j]>v[i]就说明之后的v[prime[j]*i]在后续就会被筛 */ v[prime[j]*i]=prime[j]; } } FORa(i,1,cnt) printf("%d ",prime[i]); return 0; } inline int read() { register int x(0);register int f(1);register c(gc); while(c<'0'||c>'9') f=c=='-'?-1:1,c=gc; while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=gc; return x*f; }
二、素数的相关定理
唯一分解定理(算数分布定理)
1.任何一个大于1的正整数都能唯一分解为有限个质数的乘积
2.其中参与乘积的质数的幂为单调递减数列
质数分布定理:
1.对于正实数X,定义f(x)为不大于x的质数的个数,那么 f(x)≈x/㏑x
2.第n个质数p(n)的渐进估计:p(n)=n㏑n
威尔逊定理
若p为素数,那么(p-1)!≡-1(mod p)
威尔逊的逆定理也成立:如果(p-1)!≡-1(mod p) 那么p是质数
在利用sin函数的特点,就可以构造出一个素数分布的函数曲线f(n): f(n)=sin(π*((n-1)!+1)/n).函数值为0的点都是素数所在的点
但是由于阶乘是呈爆炸增长的,其结论对于实际操作意义不大。
费马小定理
费马小定理在模数为质数的情况下,可以求逆元哦,看这篇博客吧!逆元
如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。
引理1:
若a,b,c为任意3个整数,m为正整数,且(m,c)=1,则当a·c≡b·c(mod m)时,有a≡b(mod m)。
证明:
a·c≡b·c(mod m)可得ac–bc≡0(mod m)可得(a-b)·c≡0(mod m)。因为(m,c)=1即m,c互质,c可以约去,a– b≡0(mod m)可得a≡b(mod m)
引理2:
设m是一个整数且m>1,b是一个整数且(m,b)=1。如果a[1],a[2],a[3],a[4],…a[m]是模m的一个完全剩余系,则b·a[1],b·a[2],b·a[3],b·a[4],…b·a[m]也构成模m的一个完全剩余系。
三、Miller-Rabin素数测试
简介
MillerRabinMillerRabin 素数测试是一种判断一个数是否是质数的方式。
其单次测试的时间复杂度不会超过 O(Log2N),期望为 O(LogN) ,几乎不需要额外的空间。
MillerRabinMillerRabin 素数测试不是一个确定算法,其单次测试有不超过 1414 的概率会将一个合数误判为一个素数。
但当被测试数在某一个范围内时,我们可以通过选取适当的测试底数让 MillerRabinMillerRabin 素数测试对于这个范围内的每一个数都能够正确地得出结果。
算法流程(建议拖出来阅读)
代码
#include<stdio.h> #include<stdlib.h> #define FORa(i,s,e) for(int i=s;i<=e;i++) #define FORs(i,s,e) for(int i=s;i>=e;i--) using namespace std; typedef long long ll; int const times=8; const int N=214748364; bool bz[N+1]; int prime[N+1],cnt,n,ans; int fp[]={2,3,5,7,11,13,17,19,23}; ll q_pow(ll a,ll b,ll c) { ll ans=1; for(;b;b>>=1,a=a*a%c) if(b&1) ans=ans*a%c; return ans; } bool Miller_Rabin(ll x) { if(x==2) return 1;//是2直接返回信息是素数 if(!(x&1)||x==1) return 0;//如果是1或者是2的倍数,直接返回合数 bool pass; ll d=x-1,m; while(!(d&1)) d>>=1;//将x-1分解为x-1=(2^q)*d的形式,这里q,d属于整数 ll tmp=d; for(int i=0;i<=times&&x>fp[i];i++) { d=tmp;pass=0; m=q_pow(fp[i],d,x);//生成随机数a,并判断a^d是否在mod x的情况下与1同余 if(m==1) continue;//判断是素数继续重新判断 else for(;d<x&&d>=0;m=(m*m)%x,d<<=1) if(m==x-1){pass=1;break;} if(!pass) return 0; } return 1; } void Prime() { bz[1]=1; FORa(i,2,n) { if(!bz[i]) prime[++cnt]=i; FORa(j,1,cnt) { if(i*prime[j]>n) break; bz[i*prime[j]]=true;//注意此时之前还没有被筛了,一个质数有关筛要等循环i来筛 if(i%prime[j]==0) break; } } } int main() { scanf("%d",&n); Prime(); FORa(i,1,n) if(bz[i]==Miller_Rabin(i)) ans++,printf("%d\n",i); printf("%d ",ans); return 0; }
四、欧拉定理
欧拉函数:
对于正整数n,欧拉函数是小于等于n的数中与n互质的个数,它又称为Euler's totient function、φ函数、欧拉商数等
从欧拉函数引伸出来在环论方面的事实和拉格朗日定理构成了欧拉定理的证明。
引理1:
①如果n为某一个素数p,则φ(p)=φ(p-1)
②如果n为某一个数素数p的幂次p^a,则φ(p^a)=(p-1)*( p^(a-1) )
③如果n为任意两个互质的数a,b的积,则φ(a*b)=φ(a)*φ(b)
证明:
①显然成立
②因为比p^a小的数一共有p^a-1个数,而其中所有能够被p整除的数一共有p^(a-1)-1个,所以φ(p^a)=(p-1)*( p^(a-1) )
③在比a*b小的正整数有a*b-1个,只有那些既与a互质又与b互质的数才满足条件,φ(a*b)=φ(a)*φ(b)
引理2:
设n=p1^a1*p2^a2*````*pk^ak为正整数的素数幂乘积形式
则φ(n)=n(1−1/p1)(1−1/p2)*...*(1−1/pk)
欧拉定理:
五、Pollard Pho求大数因子
六、相关转载与推荐文章(十分感谢这些博主)
Miller_Rabin素数测试[Fermat小定理][二次探测定理][同余式][Wilson定理]