素数
定义
一个大于1的自然数,除了1 和它自身外,不能整除其他自然数的数
性质
- 素数有无穷多个
- 存在任意长的连续数,其中所有数都是合数(相邻素数之间的间隔任意大)
- 随着n的增大素数越来越稀疏
哥德巴赫猜想
任意大于2的正偶数都可以写成两个素数的和
一个充分大偶数必定可以写成一个素数加上最多由2个质因子所组合的合成数,简称为(1+2)
贝特朗假设
对任意 n≥1 , n 到 2n 之间(不包括n)至少存在一个质数。
∀ n≥1,∃ p∈P, n<p≤2n
推论:2n 之内至少存在n个质数
素数定理
设n≥2,以 π(x) 表示不超过 x 的素数的个数,当 n→∞ 时,
π(n)∼∫2nlnx1dx
推论:从不大于 n 的自然数随机选一个,它是素数的概率大约是 lnn1
算术基本定理
任何一个大于1的正整数都能唯一分解为有限个质数的乘积,可写作:
N=p1c1p2c2⋯pmcm
其中 ci 都是正整数,pi都是质数,且满足p1<p2<⋯<pm。
推论:N的正约束的集合可写作:
{p1c1p2c2⋯pmcm},其中0≤bi≤ci
N 的正约数个数为
(c1+1)∗(c2+1)∗⋯∗(c+1)=i=1∏m(ci+1)
N 的正约数的和为:
(1+p1+p12+⋯+p1c1)∗⋯∗(1+pm+pm2+⋯+pmcm)=i=1∏m(j=0∑ci(pi)j)
勒让德定理
N!中质因子 p 的个数为:
⌊pN⌋+⌊p2N⌋+⋯+⌊p⌊logpN⌋N⌋=pk≤N∑⌊pkN⌋
欧拉函数
1~N 中与 N 互质的数的个数被称为欧拉函数,记为 φ(N)。
在算术基本定理中, N=p1c1p2c2⋯pmcm, 则:
φ(N)=N∗p1p1−1∗p2p2−1∗⋯∗pmpm−1=N∗i=1∏m(1−pi1)
原理:由容斥原理可得,设 p,q 是 N 的质因子,1~N 中共有 pN 个 p 的倍数,qN 个 q 的倍数,同时有pqN 个 pq 的倍数,因此剩下数的个数有:
N−pN−qN+pqN=N∗(1−p1−q1+pq1)=N∗(1−p1)(1−q1)
性质
- p 为素数 ,则 φ(p)=p−1,φ(pk)=(p−1)pk−1
- ∀n>1,1~n 中与 n 互素的数的和为 2n∗φ(n)
- 若 a,b 互素,则 φ(ab)=φ(a)φ(b)
- p 为素数,若 p∣n 且 p2∣n, 则 φ(n)=φ(pn)∗p
- p 为素数,若 p∣n 但 p2∤n, 则 φ(n)=φ(pn)∗(p−1)
- ∑d∣nφ(d)=n
积性函数
如果当 a,b 互素时,有 f(ab)=f(a)∗f(b),那么称函数 f 为积性函数。
性质
若是 f 积性函数,且在算术基本定理中 n=∏i=1mpici ,则 f(n)=∏i=1mf(pici)
筛法
埃拉托斯特尼筛法 O(nloglogn)
点击查看代码
void primes(ll n){
for(int i=2;i<=n;i++){
if(v[i])continue;//如果被筛过
cout<<i<<endl;//剩下的为素数
for(int j=i;j*i<=n;j++){//筛掉其倍数
v[i*j]=1;
}
}
}
欧拉筛/线性筛 O(n)
确保每个合数只会被其最小质因子筛过
点击查看代码
void primes(ll n){
for(int i=2;i<=n;i++){
if(v[i]==0){v[i]=i;prime[++cnt]=i;}
for(int j=1;j<=cnt;j++){
//prime[j]应为比i筛过的最小质因子小
if(prime[j]>v[i]||i*prime[j]>n)break;
v[i*prime[j]]=prime[j];
}
}
}
素性测试
二次探测定理
若 p 为素数, a2≡1(modp) ,那么 a≡±1(modp)
米勒-拉宾算法
利用费马小定理对数进行素性测试,并通过二次探测定理检验,具有极高正确概率的算法。判断一个数 p 是否为素数,将其 p−1 分解成 t×2k, 先对 at(modp) 进行费马检验,再进行最多 t 次平方操作,每次根据二次探测定理检验
点击查看代码
bool miller_rabin(ll p) {
if(p<3||p%2==0) return p==2;
int Test[]={2,3,5,7,11,13,17,19};
int t = p - 1, k = 0;
while(!(t & 1)) k++, t >>= 1;
for(int i = 0; i < 8; i++) {
if(p == Test[i]) return 1;
ll a = quick_power(Test[i], t, p), nxt = a;
//ll x=rand()%(p-2)+2,a=quick_power(x,t,p);
for(int j = 1; j <= k; j++) {
nxt = (a * a) % p;
if(nxt == 1 && a != 1 && a != p - 1) return 0;
a = nxt;
}
if(a != 1) return 0;
}
return 1;
}