博主是个数学菜鸡,它考试几乎没及格过,但是他牛逼的同学们要他写笔记,so,他只能硬着头皮屑了,咕咕咕,可能有很多错误还望海涵!
有关素数的小结
质数(prime number)又称素数,有无限个。一个大于1的自然数,除了1和它本身外,不能被其他自然数整除,换句话说就是该数除了1和它本身以外不再有其他的因数;否则称为合数。来自360百科
素数好玩的性质:
- 存在任意长的一段连续数,其中的所有数都是合数(相邻素数之间的间隔任意大)
证明:当\(0<a<=n\)时,\(n!+a\)能被a整除。长度为n-1的数列\(n!+2, n!+3, n!+4, …, n!+n\)中,所有的数都是合数。这个结论对所有大于1的整数n都成立,而n可以取到任意大。
- 所有大于\(2\)的素数都可以唯一地表示成两个平方数之差。
证明:大于\(2\)的素数都是奇数。假设这个 数是\(2n+1\)。由于\((n+1)^2=n^2+2n+1\),\((n+1)^2\)和\(n^2\)就是我们要找的两个平方数。下面证明这个方案是唯一的。如果素数p能表示成 \(a^2-b^2\),则\(p=a^2-b^2=(a+b)(a-b)\)。由于p是素数,那么只可能\(a+b=p\)且\(a-b=1\),这给出了a和b的唯一解。
- 当n为大于2的整数时,\(2^n+1\)和\(2^n-1\)两个数中,如果其中一个数是素数,那么另一个数一定是合数。
证明:\(2^n\)不能被3整除。如果它被3除余1,那么\(2^n-1\)就能被\(3\)整除;如果被\(3\)除余2,那么\(2^n+1\)就能被\(3\)整除。总之,\(2^n+1\)和\(2^n-1\)中至少有一个是合数。
证明素数无限多
先来个简单的:
2是素数,23是素数,233是素数,2333是素数,23333是素数,233333是素数,2333333是素数,23333333是素数,233333333是素数........
其实以上是放屁....
正确的证明:
经典反证法:
假设素数是有限的,假设素数只有有限的\(n\)个,最大的一个素数是p
设q为所有素数之积加上1,那么,\(q = ( 2 * 3 * 5 * …… * p )+ 1\)不是素数
那么,q可以被\(2、3、……p\)中的数整除
而q被这\(2、3、……、p\)中任意一个整除都会余1,与之矛盾
所以,素数是无限的.
素数计数函数:
好想没什么用,看看就行了...咕咕咕
小于或等于 \(x\) 的素数的个数,用 \(\pi(x)\) 表示。随着 \(x\) 的增大,有这样的近似结果: \(\pi(x) \sim \frac{x}{\ln(x)}\)
素数判定
暴力做法
自然可以枚举从小到大的每个数看是否能整除
bool Prime(int x) {
if (x < 2) return 0;
for (int i = 2; i < x; ++i)
if (x % i == 0)
return 0;
return 1;
}
小优化
只需要循环到\sqrt{a},因为因子是成对出现的
bool iPrime(int x) {
if (x < 2) return 0;
for (int i = 2; i * i <= x; ++i)
if (x % i) return 0;
return 1;
}
要写i*i<=x,而不是i<=sqrt(x);
因为sqrt比直接乘要慢的多
Miller-Rabin 素性测试
其实我也不会,你可以去看看lyq大佬的博客,顺便膜拜她!
我就只给出代码了,咕咕咕,其实代码是来自大佬lzx的,我太弱了
我只知道该方法的原理是利用二次探测定理和飞马小定理来随机生成几个a利用费马小定理和二次探测定理来检测素数
如果p是一个素数,且0<x<p,则方程x*x≡1(mod p)的解为x=1,p-1.
#include <bits/stdc++.h>
#define int long long
using namespace std;
int test[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
int n, m;
template<class T> inline void read(T &x) {
x = 0;int f = 0;char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'),ch = getchar();
while(isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
x = f ? -x : x;
return ;
}
int qpow(int a, int b, int p) {
int ans = 1;
while (b) {
if (b & 1) ans = 1ll * ans * a % p;
a = 1ll * a * a % p, b >>= 1;
}
return ans;
}
bool miller_rabin(int p) {
if(p == 1) return 0;
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;
long long a = qpow(test[i], t, p), nx = a;
for(int j = 1; j <= k; ++j) {
nx = (a * a) % p;
if(nx == 1 && a != 1 && a != p - 1) return 0;
a = nx;
}
if(a != 1) return 0;
}
return 1;
}
signed main() {
cin>>n>>m;
long long ans=0;
for (int i = n, x; i <= m; ++i) {
if(miller_rabin(i)) ans++;
}
printf("%lld",ans);
return 0;
}
Fermat 素性测试
原理:
假如p是质数,且\(gcd(a,p)=1\),那么 \(a(p-1) ≡1(mod p)\)。即:假如a是整数,p是质数,且a,p互质(符号不会打凑合着看吧),那么\(a^(p-1)/p\)的余数恒等于1。
思路:那么,根据费马小定理的逆定理,如果们找到一个 a ,使得 a ^(n-1)%n !=1,是否就可以确定数字 n 不是质数呢?很遗憾,这是不行的,费马小定理的逆定理并不正确。
伪素数:
满足2^(n-1)%n1的合数 n 。
满足a^(n-1)%n
1的合数n叫做以 a 为底的伪素数。
Carmichael数:对于所有小于 n 的底数 a,都满足a^(n-1)%n==1 的数。(前10亿个数中,仅有600多个这样的数存在)。
在具体实现用fermat素性测试中,我们一般是随机选取有限个数的底数 a ,对 n 进行素性测试。若全部满足,则认为数字 n 是质数,若有一个不满足,则认为数字 n 不是质数。
以上部分内容参考百度
eratosthenes筛选法(埃拉托斯特尼筛法)
原理:素数的倍数是合数
步骤:
1.列出2,3,4..........100
2.选取第一个没被处理过的数(第一次为2)
3.去掉以2为因数的所有数
4.如果下一个素数小于sqrt(100)跳转到第二步
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<string>
#include<cstring>
#define ll long long int
#define MAXN 100000
using namespace std;
const int maxn=999999999;
const int minn=-999999999;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int prime[MAXN],n;
int main()
{
cin>>n;
for(int i=2;i<=n;++i)
{
if(prime[i]) continue;
cout<<i<<" ";
for(int j=1;j<=n/i;++j) prime[i*j]=1;
}
return 0;
}
欧拉筛
如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 \(O(n)\) 了,所以对上面的方法进行优化,保证每个合数被最小质因子筛掉
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<string>
#include<cstring>
#define ll long long int
#define MAXN 10000
using namespace std;
const int maxn=999999999;
const int minn=-999999999;
inline int read() {
char c = getchar();
int x = 0, f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int v[MAXN]/*最小质因子*/,prime[MAXN],m/*质数数量*/;
int main() {
int n;
cin>>n;
for(int i=2; i<=n; ++i) {
if(v[i]==0) {
v[i]=i;
prime[++m]=i;
}
for(int j=1; j<=m; ++j) {
if(prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
}
}
for(int i=1; i<=m; ++i) {
cout<<prime[i]<<endl;
}
return 0;
}
费马查表法
这个是真的不会了,所一就放张图片吧
代码:
public void femat(int n){
for(int i = 2; i <= n; i++){
if(runFermatPower(2, n-1, n) != 1){
if(i不在伪素数表中)
输出i;
}
}
}
有关gcd和lcm的小结
gcd递归定理(欧几里得)
证明:
我们假设\(a>b\)
设 \(a=bk+c\) ,显然有 \(c=a \bmod b\) 。设 \(d|a\) \(d|b\) ,则 \(c=a-bk\) \(\frac{c}{d}=\frac{a}{d}-\frac{b}{d}k\), 由右边的式子可知 \(\frac{c}{d}\) 为整数,即 \(d|c\) 所以对于 \(a,b\) 的公约数,它也会是 \(a \bmod b\) 的公约数。
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
算术基本定理
每一个正整数都可以表示成若干整数的乘积,这种分解方式在忽略排列次序的条件下是唯一的。
即:
设 \(a = p_{a_1}^{k_{a_1}}p_{a_2}^{k_{a_2}} \cdots p_{a_s}^{k_{a_s}}\) , \(b = p_{b_1}^{k_{b_1}}p_{b_2}^{k_{b_2}} \cdots p_{b_s}^{k_{b_s}}\)
我们发现,对于 \(a\) 和 \(b\) 的情况,二者的最大公约数等于
\(p_1^{k_{\min(a_1, b_1)}}p_2^{k_{\min(a_2, b_2)}} \cdots p_s^{k_{\min(a_s, b_s)}}\)
最小公倍数等于
\(p_1^{k_{\max(a_1, b_1)}}p_2^{k_{\max(a_2, b_2)}} \cdots p_s^{k_{\max(a_s, b_s)}}\)
由于 \(a + b = \max(a, b) + \min(a, b)\)
所以得到结论是 \(\gcd(a, b) \times \operatorname{lcm}(a, b) = a \times b\)
lcm(a,b)的求法
先求出\(gcd(a,b)\),然后根据\(gcd(a,b) \times lcm(a,b)=a*b\)求解
扩展欧几里得-exgcd()
求 \(ax+by=\gcd(a,b)\) 的一组可行解
证明
\(ax_1+by_1=\gcd(a,b)\)
\(bx_2+(a\bmod b)y_2=\gcd(b,a\bmod b)\)
由欧几里得定理可知: \(\gcd(a,b)=\gcd(b,a\bmod b)\)
\(ax_1+by_1=bx_2+(a\bmod b)y_2\)
\(a\bmod b=a-(\lfloor\frac{a}{b}\rfloor\times b)\)
所以 \(ax_1+by_1=bx_2+(a-(\lfloor\frac{a}{b}\rfloor\times b))y_2\)
\(ax_1+by_1=ay_2+bx_2-\lfloor\frac{a}{b}\rfloor\times by_2=ay_2+b(x_2-\lfloor\frac{a}{b}\rfloor y_2)\)
因为 \(a=a,b=b\) ,所以 \(x_1=y_2,y_1=x_2-\lfloor\frac{a}{b}\rfloor y_2\)
来自百度
exgcd的求法:
就直接套gcd递归定理就行了 在这个过程中计算 \(x,y\) 即可
代码解释请左转百度
int Exgcd(int a, int b, int &x, int &y) {//此处也可用全局变量
if (!b) {
x = 1;
y = 0;
return a;
}
int d = Exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - (a / b) * y;
return d;
}
费马小定理和欧拉定理
话说上面好像用到了,怎么到现在才写,咕咕咕
欧拉定理
若 \(\gcd(a, m) = 1\) ,则 \(a^{\phi(m)} \equiv 1 \pmod{m}\) 。
证明:
将\(1~n\)中与\(n\)互质的数按顺序排布:$x1,x2……xφ(n) \((显然,共有\)φ(n)$个数)
我们考虑这么一些数:
\(m1=a*x1;m2=a*x2;m3=a*x3……mφ(n)=a*xφ(n)\)
(1)
这些数中的任意两个都不模n同余,因为如果有\(mS≡mR (mod n)\) (这里假定mS更大一些),就有:
\(mS-mR=a(xS-xR)=qn\),即n能整除\(a(xS-xR)\)。但是\(a\)与\(n\)互质,a与n的最大公因子是1,而xS-xR<n,因而左式不可能被n整除。也就是说这些数中的任意两个都不模n同余,\(φ(n)\)个数有\(φ(n)\)种余数。
(2)
这些数除n的余数都与n互质:
我们知道\(a\),\(xi\)与\(n\)互质,则\(a × xi\) 与n互质,根据欧几里得算法,则\(n\)与\((a × xi) %n\)也互质 。
那么这些数除n的余数,都在\(x1,x2,x3……xφ(n)\)中,因为这是\(1~n\)中与\(n\)互质的所有数,而余数又小于\(n.\)
由(1)和(2)可知,数\(m1,m2,m3……mφ(n)\)(如果将其次序重新排列)必须相应地同余于\(x1,x2,x3……xφ(n)\)
故得出:\(m1*m2*m3……mφ(n)≡x1*x2*x3……xφ(n) (mod n)\)
或者说\(a^[φ(n)]*(x1*x2*x3……xφ(n))≡x1*x2*x3……xφ(n)\)
或者为了方便$K{a[1]-1}≡0 ( mod n ) \(这里\)K=x1x2x3……xφ(n)$
可知\(K{a^[φ(n)]-1}\)被\(n\)整除。但K中的因子\(x1,x2……\)都与\(n\)互质,所以\(K\)与\(n\)互质。那么\(a^[φ(n)]-1\)必须能被n整除,即\(a^[φ(n)]-1≡0 (mod n)\),即\(a^[φ(n)]≡1 (mod n)\),得证。
此证明是lhy教的
欧拉定理求法:
先写暴力求欧拉函数
nt Eular(int m)
{
int ret = m;
for(int i=2; i<m; i++)
{
if(m%i == 0)
ret -= ret/i;
while(m%i == 0)
{
m /= i;
}
}
if(m > 1)
ret -= ret/m;
return ret;
}
显然复杂度太高了,基本TLE 预定
所以要用线性\(o(n)\)来筛。
证明
\(phi(p) == p-1\) 因为素数\(p\)除了\(1\)以外的因子只有\(p\),所以与$ p$ 互纸的个数是 \(p - 1\)个
\(phi(p^k) == p^k - p^(k-1) == (p-1) * p^(k-1)\)
证明:
令\(n == p^k\),小于$ n \(的正整数共有\) p^k-1$ 个,其中与$ p \(不互质的个数共\) p^(k-1)-1 \(个,它们是\) 1p,2p,3p ... (p^(k-1)-1)p$
所以\(phi(p^k) == (p^k-1) - (p^(k-1)-1) == p^k - p^(k-1) == (p-1) * p^(k-1)\)
如果$i mod p == 0, 那么 phi(i * p) == p * phi(i) $
举个例子:
假设$ p = 3,i = 6,p * i = 18 = 2 * 3^2$
\(phi(3 * 6) == 18*(1-1/2)*(1-1/3) = 6\)
$p * phi(i) = 3 * phi(6) = 3 * 6 * (1-1/2) * (1-1/3) = 6 = phi(i * p) $正确
如果\(i mod p != 0\) 那么 $phi(i * p) == phi(i) * (p-1) $
oi_wiki的证明:
注意到在线性筛中,每一个合数都是被最小的质因子筛掉。比如设 \(p_1\) 是 \(n\) 的最小质因子, \(n' = \frac{n}{p_1}\) ,那么线性筛的过程中 \(n\) 通过 \(n' \times p_1\) 筛掉。
观察线性筛的过程,我们还需要处理两个部分,下面对 \(n' \bmod p_1\) 分情况讨论。
如果 \(n' \bmod p_1 = 0\) ,那么 \(n'\) 包含了 \(n\) 的所有质因子。
那如果 \(n' \bmod p_1 \neq 0\) 呢,这时 \(n'\) 和 \(n\) 是互质的,根据欧拉函数性质,我们有:
证明:
i mod p 不为0且p为质数, 所以i与p互质, 那么根据积性函数的性质 phi(i * p) == phi(i) * phi(p) 其中phi(p) == p-1
所以 phi(i * p) == phi(i) * (p-1).
再举个例子:
假设i = 4, p = 3, i * p = 3 * 4 = 12
phi(12) = 12 * (1-1/2) * (1-1/3) = 4
phi(i) * (p-1) = phi(4) * (3-1) = 4 * (1-1/2) * 2 = 4 = phi(i * p)正确
include <iostream>
#include <cstring>
using namespace std;
const int MAXN = 1e6+5;
bool flag[MAXN];///标记数组
int phi[MAXN];///欧拉函数值,i的欧拉函数值=phi[i]
int p[MAXN];///素因子的值
int cnt = 0;
void Get_phi()///筛法求欧拉函数
{
cnt = 0;
memset(flag, true, sizeof(flag));
phi[1] = 1;
for(int i=2; i<MAXN; i++)///线性筛法
{
if(flag[i])///素数
{
p[cnt++] = i;
phi[i] = i-1;///素数的欧拉函数值是素数 - 1
}
for(int j=0; j<cnt; j++)
{
if(i*p[j] > MAXN)
break;
flag[i*p[j]] = false;///素数的倍数,所以i*p[j]不是素数
if(i%p[j] == 0)///性质:i mod p == 0, 那么 phi(i * p) == p * phi(i)
{
phi[i*p[j]] = p[j] * phi[i];
break;
}
else
phi[i*p[j]] = (p[j]-1) * phi[i];///i mod p != 0, 那么 phi(i * p) == phi(i) * (p-1)
}
}
}
int main()
{
Get_phi();
int m;
while(cin>>m)///测试
{
cout<<phi[m]<<endl;
}
return 0;
}
费马小定理
若 \(p\) 为素数, \(\gcd(a, p) = 1\) ,则 \(a^{p - 1} \equiv 1 \pmod{p}\) 。
另一个形式:对于任意整数 \(a\) ,有 \(a^p \equiv a \pmod{p}\) 。
证明:
由于p是质数,所以有\(φ(p) = p-1\),代入欧拉定理即可证明。推论:对于任意正整数a,有\(a^p ≡ a (mod p)\),因为a能被p整除时结论显然成立。
扩展欧拉定理
打不出来,只能上图了
证明:
不会了,自己看下面吧:
鉴于篇幅过长所有本笔记分为上下两部分
博客太菜了,可能会有错误(特别是笔误),如发现请指明!
φ(n) ↩︎
来源:oschina
链接:https://my.oschina.net/u/4287100/blog/4267326