ACM数论入门(by FlyWhite)
前言:人类的魅力只有在他克服难关的时候才能体现——A_pi
Chapter 2 勾股数组
本原勾股数组(PPT):是一个三元组(a,b,c),其中a,b,c没有公因数,且满足
勾股数组定理
猜测
对本原勾股数组进行猜测,a,b奇偶不同,且C总为奇数
证明:
a,b都是偶数,则c也一定是偶数,那么就存在最小公因数2,不满足本原勾股数组的条件.其次假设a,b都是奇数,那么c一定为偶数,那么不妨对其进行证明
而奇数不可能等于偶数,因此a,b为奇,c为偶数显然错误。故猜测正确
猜测
c-b与c+b都是都是平方数
证明
设正整数
为
,
的公因数,即d整除
与
.则
也整除
由于b,c互素,因此d等于1或2,但d也整除
由于a为奇数,所以d不等于2,因此d=1,由此,
与
无公因数
由于
,a为平方数,(c-b)与(c+b)互素所以此二者为平方数
推论:
据上已经证明(c+b),(c-b)为平方数,因此,不妨令
则可以解出
其中
勾股数组定理:每个本原勾股数组 其中(a为奇数,b为偶数)都可以从以下公式得出: 其中
Chapter 3勾股数组与单位圆
由上一章有勾股数组
通过等式两边同除
可以得到
其在形式上近似于单位圆的方程
据本源勾股数组的所有可行解都可以表示成
则可以得出任何圆上的可行点都可以表示为
若令
其中由于x,y的实际含义被扩展到了实数域上,因此m的范围也就变成了
也可以通过对直线L:
(即横截距为-1的点)与圆
求交点得到相同的结论
圆 上坐标是有理数的点都可以由公式
其中m取有理数((-1,0)除外,此为 时的极限值)
Chapter 4高次幂之和与费马大定理
费马大定理
费马大定理: 时方程
没有正整数解
Chapter 5整除性与最大公因数
首先引入一些概念
假设m与n是整数且
.m整除n是指n是m的倍数,即存在整数k使得n=mk.如m整除n则记为
,同理如果m不整除n则记为
整除n的数称为n的因数.
欧几里得算法
已知两个整数,则我们可以求其公因数,即同时整除它们两个的数.而在众公因数中,最大公因数时常有着一些重要的性质,其定义为:两个数a与b(不全为0)的最大公因数是整除它们两个的最大数,记为
.如果
则我们称a与b互素
为了得到两个数的最大公因数,我们引入欧几里得算法:
欧几里得算法:要计算两个整数a和b的最大公因数,先令 且 ,然后计算相继的商和余数
正确性证明:
对于欧几里得算法,有一般情形可以分析有:
由于
为
的倍数因此
同理
,据此递推则可以得到a,b均为
的倍数
假设d为a与b的任意公因数,则由式子
可得
,据
得到
以此递推得到
,则d整除
那么
是任意因数的倍数,那么可以得到
就是最大的公因数
可行性证明:
每次计算商和余数
R总是在(0,B-1)中,因此B总是不断减小,最终必然会达到0
其通过c++代码的实现就是
typedef long long LL ;
LL gcd(LL a,LL b)
{
return b==0 ? a :gcd(b,a%b);
Chapter 6线性方程与最大公因数
通过简单的举例,我们可以发现,任何形如
的最小正整数等于
但是应该如何解出这个方程中的x,y呢?
通过欧几里得算法,我们或许可以发现一些灵感
据此形式,每个所有形如
的等式的R总是可以表示为a的倍数加上b的倍数,且这个倍数是可以通过欧几里得算法算法求公因数的过程中求得的,若使用c++代码实现则如下形式
线性方程定理
线性方程定理:设a与b是非0整数,令
.方程
总是有一个解
且这个解总是可以通过上述的扩展欧几里得算法得到.且方程的每一个解就可以表示为:
其中k为任意整数
证明:设存在两个正整数其公因数为1,即互素.假设存在两个解
是方程的两个解即
令一式左右两边乘上
,让二式左右两边乘上
再做差则可以得到
同理令一式左右两边乘上
,让二式左右两边乘上
则可以得到
令
则:
而对于
则可以通过将等式化作
来使得原方程的因式变换为
在C++中求解线性方程
复杂度
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
y=0;
x=1;
return a;
}
int r=exgcd(b,a%b,x,y);
int temp=y;
y=x-(a/b)*y;
x=temp;
return r;
}
Chapter 7因式分解与算术基本定理
引理1
引理1:令p为素数,假设p整除乘积ab,则p整除a或整除b(或既整除a又整除b)
证明:已知p整除ab,假设p不整除a
先分析
由于p为质数,且其整除p所以
要么等于p要么等于1,由于p不整除a因此,
则下方程一定有解
令方程两边乘上b则可以得到
显然可得,p整除pb,p整除ab因此,p整除b.证毕.
素数整除性质
素数整除性质:假设素数p整除乘积
,则p整除
其中至少一个因数.
证明:由引理1可得p整除
或
,若p不整除
则则必然整除
,进一步p整除
或
通过不断的递归,最终可以得到p至少整除
其中至少一个因数.
偶数世界( 地带)
假想我们是一个生活在偶数世界中的生物,在这个世界中存在的仅有的数是偶数,那么一些我们原本看来的“常识”就会变得可贵起来了(比如我们后面紧接着要介绍的算术基本定理).
在偶数世界我们仍然可以对数进行、减、乘运算,因为偶数的和、差、积仍然是偶数,同样也可以讨论整除性.如果存在数k使得
,则称m
整除n,但是由于现在处于偶数世界,k只能是偶数.因此,在偶数世界中,素数的定义也就变成了:
如果任何偶数都不整除p,那么称该偶数为 素数,如:2,6,10,14,18,22,26,30.
让我们重新在偶数世界中分析刚才在的引理一,即“令p为一素数,假设p整除乘积ab,那么p必然整除a或者b”假设 素数为6,a=10,b=18.因为 所以数6 整除,但是显然6不 整除10也不 整除18.可以说明,引理一在偶数世界中失效
再如在我们原本世界中的常识“每个整数
可唯一分解成素数乘积
”在偶数世界中也是不成立的如:
其中18,10,6,30均是
素数,因此,就有了两种素数乘积.
此处引入偶数世界的目的是,让我们明白,我们平常看到的那些习以为常的定理,也并不可以盲目地相信,一些一定成立的事实有时却也需要更多的考察与研究
算术基本定理
算术基本定理:每个整数
可唯一分解成素数乘积
证明:算术基本定理可以分解成两个断言
- 断言1:数n可以以某种方式分解成素数乘积
- 断言2:仅有一种这样的分解
先证明第一个断言1,此处我们采用数学归纳法来证明
- 当n=2时,2=2显然成立
- 当n=3时,3=3显然成立
- 假设直到N的每一个数对第一个断言都成立。
此时讨论N+1的情况
当N+1为素数时,其本身为素数,故定可分解为素数的乘积
当N+1为合数时其至少可以分解为 .但是已知 对第一个断言成立因此,N+1也就可以分解成
- 所以N+1可以分解成素数的乘积,第一个断言成立
接下来证明断言2:
假设可以将n分解成两个两种形式的素数乘积如下
假如我们可以证明序列p和序列q在重排之后是完全一样的,我们就可以证明n仅有一种素数分解的方式
由于
因此
,据素数整除定理,则
至少整除
中的一个,但由于
也为质数因此,至少存在一个
,由于元素的顺序并不造成影响,不如将
与
调换值,再据此将
与
消去,那么就得到了下式
通过这样的方法不断消去,因为不可能出现一边被消净,而一边还有情况(这样就代表一些素数的乘积为0了,这是不可能发生的),以此就证明了
其中r=s且通过重排一定可以得到
由此断言二就得到了证明
在C++中唯一分解一合数(pollard_rho算法)
复杂度 大约可以分解1e18一百次
ll pollard_rho(ll n,ll c)
{
int i=1,k=2;
ll x=rand()%n;ll y=x;
for(;;)
{
i++;
x=(qmul(x,x,n)+c)%n;
ll d=gcd(abs(x-y),n);
if(d!=1 && d!=n) return d;
if(y==x) return n;
if(i==k) y=x,k<<=1;
}
}
void find(ll n)
{
if(n==1) return;
if(miller_rabin(n))
{
a[++num]=n;
return;
}
ll d=n;
while(d>=n) d=pollard_rho(n,rand()%(n-1)+1);
find(d);
while(n%d==0) n/=d;
find(n);
}
Chapter 8同余式
如果m整除a-b那么我们称作a与b模m同余并记之为
其中m称为同余式的模.具有相同模的同余式,再许多方面表现得像通常的等式.其满足同余式的加减法、乘法,如:
则:
但是同余式的除法仅在除数与模数互素时成立,即
仅在
时成立
线性同余定理
线性同余定理:设a,c与m均是整数, ,并设
- 如果 则同余式 无解
- 如果 则同余式 恰好有g个不同的解.要求这些解则先要求线性方程
的一个解(这可以通过扩展欧几里得算法得出)
.则
是该同余式的一个解,而解的完全集由
给出
推导过程:给出同余式
再设一整数y则原同余式有解等价于线性方程
有解
不妨设 这个方程只有当 时有解,因此原同余式也仅在此时有解
现讨论有解情况,因有解故
不妨将原式转化为求
的解
再将等式左右两边乘上
则得到
则
是原同余式的解进一步探究其他的解,假设存在其他的解设为
则
,故
故
由于g为a,m的最大公约数,故
之间并无因数,因此
故有
模 多项式根定理
**模
多项式根定理:**设p为素数
是次数为
的整系数多项式,且
,则同余式
最多有d个模p不同余的解
证明:对本定理我们采用反证法来证明.
-
提出假设 :至少存在一个首项系数不被p整除的整系数多项式 ,使得同余式 模p不同余的根的个数大于 的次数
-
在这样的多项式中选出一个次数最低的,设为
让
为此同余式的至少一部分解先对任意值r, 是可约的,写作
由于
故可以提出(x-r),得到:
令那个次数为d-1的多项式为G(x)
使得
取 ,由 据同余式减法有
取某个解 将有
因为 与 不同余,由于p为素数,故 通过同余式除法,故则此时 均为 的解这样F(x)就不再是次数最低的“同余式 模p不同余的根的个数大于 的次数”,故原假设错误,这表明,无多项式模p不同余p的根的数量大于其次数.
-
证毕
Chapter 9 同余式、幂与费马小定理
费马小定理
费马小定理:设p为素数,a是任意整数,且
(即
),则有
对费马小定理进行证明之前我们需要先证明一下别的引理
引理9.2
引理9.2:设p是素数,a是任何整数且
,则数
与数列
相同,尽管他们的次序不同
证明:
显然都不整除于p,而不整除p的数取模有且仅有1,2,…(p-1)种答案,因此只需证明a,2a,3a…(p-1)a(mod p)互不相同即可.假设存在ja,ka,同余即有
即有
由于
故
由于
所以
由于p为素数,所以,除非j=k否则不同余,故得出a,2a,3a…(p-1)a(mod p)互不相同.故原命题得证.
有了这个引理,我们就可以很方便地证明费马小定理
证明(费马小定理):据引理9.2可知
与
相同那么
即有
其中p为质数,所以
故可作除法,两边同除去(p-1)!就有
得证.
Chapter 10 同余式、幂与欧拉公式
欧拉公式
欧拉公式:如果
则
其中
为欧拉函数,意指区间[1,m]中与m互质的数的数量
证明:设
为区间[1,m]中与m互质的数,则近似于引理9.2有
由于
故可将两边的
除去,那么就有
得证
补充:乘法逆元
逆元的意义:对于一些题目会要求把结果模一个数,通常是一个较大的质数,对于加减乘法通过同余定理可以直接拆开计算,但对于 这个式子,是不可以写成 的,但是可以写为 ,其中 表示b的逆元.
费马小定理求法
由费马小定理可知,当p为质数,且 时有 分解后就可以写成
故可知当p为质数,a,p互质时a关于p的逆元就是 ,因此通过快速幂求解即可.复杂度
费马小定理求逆元模板
LL qkpow(LL a,LL p,LL mod)
{
LL t=1,tt=a%mod;
while(p)
{
if(p&1)t=t*tt%mod;
tt=tt*tt%mod;
p>>=1;
}
return t;
}
LL getInv(LL a,LL mod)
{
return qkpow(a,mod-2,mod);
}
欧拉公式求逆元
与费马小定理同理,当a,p互质,但p不一定为合数时需要采用欧拉公式使得 为 将费马小定理中的幂次mod-2改为 即可,其中 的求法在后面的Chapter11将会介绍.时间复杂度
扩展欧几里得算法求逆元
当所给的数和模数无法满足时,我们可以选择解线性方程的方式来解出一个数对应模数的逆元.
形如
我们可以转化为
通过扩展欧几里得算法求出x即是a在对应p时的逆元,复杂度
扩展欧几里得算法求逆元模板
LL exgcd(LL a,LL b,LL &x,LL &y)//扩展欧几里得算法
{
if(b==0)
{
x=1,y=0;
return a;
}
LL ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
LL getInv(int a,int mod)//求a在mod下的逆元,不存在逆元返回-1
{
LL x,y;
LL d=exgcd(a,mod,x,y);
return d==1?(x%mod+mod)%mod:-1;
}
递推法求逆元
令模数为p原数为i,令t=p/i,k=p%i,则有
即
当i为p的因数时,逆元不存在,不讨论,当i不为p的因数时自然有
故可对原式做除法
将t,k重新替代回,则有
故可在O(n)的复杂度内推出逆元
递推法求逆元模板
LL inv[mod+5];
void getInv(LL mod)
{
inv[1]=1;
for(int i=2;i<mod;i++)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
补充:威尔逊定理
威尔逊定理:当且仅当p为素数,
证明:
选择证明威尔逊定理的逆反命题即:若p为合数,则
若p为合数但不为平方数,则一定可以分解为乘积a*b且 则 一定包含a,b则一定有
若p为合数且p为平方数,那么由于 所以 中一定包含 故一定有
Chapter 11欧拉 函数与中国剩余定理
函数公式
函数公式:
证明:
首先证明p为素数, 的情况
- 当m为p为素数,k=1时,每个整数a( )均与p互素,因此有
- 当m为p为素数,k>1时,在区间 中仅有 与 有公因数p因此
证明当 时的情况.我们找出一个包含 个元素的集合,与一个包含 个元素的第二个集合,若两个集合有相同个数的元素,则原命题成立
第一个集合为
第二个集合为
假设建立一种对应关系有数a对应数对
接下来我们只需要证明:
- 第一个集合的不同的数对应第二个集合不同的数对
- 第二个集合的每个数对适合第一个集合的某个数
试先证明1,我们取第一个集合中的两个数
与
,假设它们在第二个集合有相同的映射,即
由于m与n互素,故据上二式也一定有
故
,由此完成了对1的证明
再来证明2,2若是正确的则须保证,对b,c的任意已知值,至少可以求得一个整数a满足
在证明这个问题之前,我们需要先引出一则新的定理
中国剩余定理
中国剩余定理:设m与n是整数,gcd(m,n)=1,b与c为任意整数.则同余数组
恰好有一个解
证明:首先解 ,其解定有形如 由此带入第二个同余式 由于gcd(m,n)=1据线性同余定理,恰好仅有一个解.得证.
**证明补充( 函数公式)**据中国剩余定理则2成立,故 函数公式得证.
在C++中快速求得
欧拉函数筛
求出 的 ,将初始值都先设为1,通过从前向后遍历并让每个含有质数因子i的数都乘上(i-1)可以在O(n)的复杂度下求得(1,n)中所有数的欧拉函数值.时间复杂度
void euler(int n)
{
phi[1]=1;//1要特判
for (int i=2;i<=n;i++)
{
if (flag[i]==0)//这代表i是质数
{
prime[++num]=i;
phi[i]=i-1;
}
for (int j=1;j<=num&&prime[j]*i<=n;j++)//经典的欧拉筛写法
{
flag[i*prime[j]]=1;//先把这个合数标记掉
if (i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];//若prime[j]是i的质因子,则根据计算公式,i已经包括i*prime[j]的所有质因子
break;//经典欧拉筛的核心语句,这样能保证每个数只会被自己最小的因子筛掉一次
}
else phi[i*prime[j]]=phi[i]*phi[prime[j]];//利用了欧拉函数是个积性函数的性质
}
}
}
求单个数的欧拉函数值
直接求小于或等于n,且与n互质的个数.时间复杂度
int oula(int n)
{
int rea=n;
for(int i=2; i*i<=n; i++)
if(n%i==0)
{
rea=rea-rea/i;
do
n/=i;
while(n%i==0);
}
if(n>1)
rea=rea-rea/n;
return rea;
}
ACM中的中国剩余定理问题
即解同余方程组,如:
其中
为两两互质的整数,求x的最小非负整数解
解:
令 由于 互质,故M为所有 的最小公倍数
令 为同余方程 的解,
由于 均有
故对同余方程两边乘上 则有
故x有通解
故有通解
故其最小非负整数解就是
其中 即是 在模数为 时的逆元,故用扩展欧几里得算法算出ti再做累加即可得出答案
void exgcd(int a,int b,int &x,int &y)
{
if(b==0){ x=1; y=0; return;}
exgcd(b,a%b,x,y);
int tp=x;
x=y; y=tp-a/b*y;
}
int china()
{
int ans=0,lcm=1,x,y;
for(int i=1;i<=k;++i) lcm*=b[i];
for(int i=1;i<=k;++i)
{
int tp=lcm/b[i];
exgcd(tp,b[i],x,y);
x=(x%b[i]+b[i])%b[i];//x要为最小非负整数解
ans=(ans+tp*x*a[i])%lcm;
}
return (ans+lcm)%lcm;
}
补充:扩展中国剩余定理
前面已经解决了,当
互素时线性同余方程
的解.那么当 不互素时?
如何解余数不互质的线性同余方程组:
首先对前面两个式子进行处理
我们可以将原同余式转化成左式
通过移项可以转化成 据前面的扩展欧几里得可知此方程在 时有解,且可以解出 同时满足一式和二式.
此时可以得到一个同时满足上面两个式子的 我们取 来代表
来构造一个数d满足 这样就可以用同余式 来代替原来两个式子
由此,令 即可.
据上,则可以将原式合并成下式
据此将所有的等式合并即可得出最终答案.
C++中解决扩展中国剩余定理
求模线性方程组 的最小整数解
LL work(){
LL M=m[1],A=a[1],t,d,x,y;int i;
for(i=2;i<=n;i++){
d=exgcd(M,m[i],x,y);
if((a[i]-A)%d)return -1;
x*=(a[i]-A)/d,t=m[i]/d,x=(x%t+t)%t;
A=M*x+A,M=M/d*m[i],A%=M;
}
A=(A%M+M)%M;
return A;
}
Chapter 12素数
无穷多素数定理
无穷多素数定理:存在无穷多的素数
证明:假设有素数表 我们使其相乘并+1,令为A.若A为素数则为素数表添加了一个新数,如A不是素数则其必然存在一个因数不为 中的任一项否则就必须整除1,但这与 为素数矛盾
模4余3素数定理
模4余3素数定理:存在无数多的模4余3的素数
证明:证明繁琐,且与无穷多素数定理类似,故略去
算术级数的素数迪利克雷定理
算术级数的迪利克雷定理:设a与m是整数,
.则存在无穷多个素数模m余a,即存在无穷多的素数满足
证明:须使用复数微积分的方法证明,过于复杂,略去
Chapter 13素数的计数
注:本章的一些内容的证明过于困难,甚至至今无法证明,故不会写出证明步骤,见谅.在此写出是因为类似哥德巴赫猜想通过计算机的的验证已经可以证明数据在 的数据范围内都是成立的,因此在ACM的题目中可能会有一定的作用,也为了提高数学素养,故作一定的介绍
素数定理
素数定理:当x很大时,小于x的素数个数趋近于
即:
哥德巴赫猜想
强哥德巴赫猜想:任一大于2的偶数都可写成两个素数之和
弱哥德巴赫猜想:任一大于7的奇数都可写成三个质数之和
哥德巴赫猜想的暴力验证方法
强哥德巴赫猜想:暴力把偶数除2,结果向两边扩展,这种方法基本上在longlong 的数据范围内不会超时
弱哥德巴赫猜想:把奇数分解为3+偶数则变成了对原奇数-3验证强哥德巴赫猜想,最后得到的素数就是3,和原奇数-3和作强哥德巴赫猜想分解的结果
孪生素数猜想
孪生素数猜想:存在无穷多的素数p使得p+2也时素数
猜想
猜想:存在无限多形如 的素数
Chapter 14梅森素数
命题14.1:对于整数 与 ,若$a^n -1 $是素数,则a必等于2,且n必为素数
我们将形如 的素数称为梅森素数
Chapter 15 梅森素数与完全数
什么是完全数?
完全数:因数之和与原本的数相等,则称这个数为完全数
欧几里得完全数公式
欧几里得完全数公式:如果 为素数,则 为完全数
证明:
设
则原数就为
,其有因数
求和则有
由于 ,所以和就为
函数公式
函数公式:
,则其有公式
证明:与欧拉函数类似,构建有
个元素的集合,和有
个元素的集合,证明两个集合有相同的和即可,此处略
欧拉完全数定理
欧拉完全数定理:如果n是偶完全数,则n形如
其中
为梅森素数
证明:可以证,但没必要
幂模m与逐次平方法
引:在ACM竞赛的中我们经常会需要对某些数求一个极大的幂次,在幂次比较大时我们可以选择快速幂的方法,但有时,由于幂次实在过大导致O(log(n))的复杂度也无法使程序在时限内通过,此时我们就需要通过一些数学上的方法来优化复杂度
逐次平方法
也即快速幂通过二分方法,来使得复杂度降到O(log(n))
当幂次可以以数字型读取时:
LL pow(LL a, LL n, LL p) //快速幂 a^n % p
{
LL ans = 1;
while(n)
{
if(n & 1) ans = ans * a % p; //若不取模就去掉p
a = a * a % p;
n >>= 1;
}
return ans;
}
当幂次大到只能用字符串读取时
#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
long long quick_mod(long long a,long long b)
{
long long ans=1;
while(b){
if(b&1){
ans=(ans*a)%mod;
b--;
}
b/=2;
a=a*a%mod;
}
return ans;
}//内部也用快速幂
long long quickmod(long long a,char *b,int len)
{
long long ans=1;
while(len>0){
if(b[len-1]!='0'){
int s=b[len-1]-'0';
ans=ans*quick_mod(a,s)%mod;
}
a=quick_mod(a,10)%mod;
len--;
}
return ans;
}
int main(){
char s[100050];
int a;
while(~scanf("%d",&a)) //求a^s%mod
{
scanf("%s",s);
int len=strlen(s);
printf("%I64d\n",quickmod(a,s,len));
}
return 0;
}
欧拉降幂法
欧拉降幂公式
欧拉降幂公式:当
时有:
证明过于繁琐,故略去
#include <bits/stdc++.h>
#define ll __int64
#define mod 10000000007
using namespace std;
char a[1000006];
ll x,z;
ll quickpow(ll x,ll y,ll z)
{
ll ans=1;
while(y)
{
if(y&1)
ans=ans*x%z;
x=x*x%z;
y>>=1;
}
return ans;
}
ll phi(ll n)
{
ll i,rea=n;
for(i=2;i*i<=n;i++)
{
if(n%i==0)
{
rea=rea-rea/i;
while(n%i==0)
n/=i;
}
}
if(n>1)
rea=rea-rea/n;
return rea;
}
int main()
{
while(scanf("%lld %s %lld",&x,a,&z)!=EOF)
{
ll len=strlen(a);
ll p=phi(z);
ll ans=0;
for(ll i=0;i<len;i++)
ans=(ans*10+a[i]-'0')%p;
ans+=p;
printf("%lld\n",quickpow(x,ans,z));
}
return 0;
}
Chapter 17计算模m的k次根
如何计算模m的k次根
如何计算模m的k次根:设b,k,m为已知整数,满足
则可以通过下步骤得出同余式
- 计算
- 求满足 的正整数u与v
证明:
如何使用C++求模p的k次根
// How to calculate x^k (mod p) =
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
void extgcd(long long a,long long b,long long &x,long long &y)
{
if(b == 0) x = 1,y = 0;
else{
extgcd(b,a%b,y,x);
y -= a/b*x;
}
}
long long Phi(LL n)
{
long long ans = n;
for(int i = 2;i < n; ++i){
if(n%i==0) {
ans = ans/i*(i-1);
while(n % i == 0) n /= i;
}
}
if(n != 1)
ans = ans/n*(n-1);
return ans;
}
long long qpow(long long a,long long b,long long m){
long long ans = 1;
a %= m;
while(b>0){
if(b&1) ans = ans*a%m;
a = a*a%m;
b >>= 1;
}
return ans;
}
int main(void)
{
// init();
LL x,k,b,m;
LL xx,yy;
while(cin>>k>>b>>m){
long long Phim = Phi(m);
// cout<<Phim<<endl;
extgcd(k,Phim,xx,yy);
xx = (xx%Phim+Phim)%Phim;
cout<<xx<<endl;
x = qpow(b,xx,m);
cout<<x<<endl;
cout<<qpow(x,k,m)<<endl;
}
return 0;
}
Chapter 19素性测试与卡米歇尔数
前言:根据费马小定理,假如p为素数,那么对所有的整数a都有 ,则可以通过不断选取a若出现不满足这个条件则这个数就是合数,但是存在一种米歇尔数,其虽然为合数,但是任意的a都满足上述同余式
米歇尔数
卡米歇尔数:卡米歇尔数是可冒充素数的一种合数,其没有任何可以用费马测试法判断出为合数的证据
素数的一个性质
设p为一个奇素数,记
设a为不被p整除的整数.则下述的两个条件之一成立:
- 数 之一模p余-1
证明:据费马小定理
,则数表
最后一个数模p余一(
),由于数表中每一个数都是前一个数的平方,因此:
- 表中的第一个数模p余1
- 表中一些数模p不为1,但是其平方模p余1,因此其应该与-1同模
合数的拉宾-米勒测试
拉宾-米勒测试:设n为奇素数,记 ,q为奇数,对不被n整除的某个a,若下述两个条件都不满足,则n为合数
- 对所有i=0,1,2,3,…,k-1,
即不满足上述的那个素数性质.经过一定的证明,有结论,如果n为奇合数,则1到n-1之间至少有75%的数可以作为拉宾-米勒测试的证据因此验证成功的概率就是 其中k为验证的次数,基本上5次之后就可以正确得进行验证
C++代码实现拉宾米勒素数测试
// 18位素数:154590409516822759
// 19位素数:2305843009213693951 (梅森素数)
// 19位素数:4384957924686954497
LL prime[6] = {2, 3, 5, 233, 331};
LL qmul(LL x, LL y, LL mod) { // 乘法防止溢出, 如果p * p不爆LL的话可以直接乘; O(1)乘法或者转化成二进制加法
return (x * y - (long long)(x / (long double)mod * y + 1e-3) *mod + mod) % mod;
}
LL qpow(LL a, LL n, LL mod) {
LL ret = 1;
while(n) {
if(n & 1) ret = qmul(ret, a, mod);
a = qmul(a, a, mod);
n >>= 1;
}
return ret;
}
bool Miller_Rabin(LL p) {
if(p < 2) return 0;
if(p != 2 && p % 2 == 0) return 0;
LL s = p - 1;
while(! (s & 1)) s >>= 1;
for(int i = 0; i < 5; ++i) {
if(p == prime[i]) return 1;
LL t = s, m = qpow(prime[i], s, p);
while(t != p - 1 && m != 1 && m != p - 1) {
m = qmul(m, m, p);
t <<= 1;
}
if(m != p - 1 && !(t & 1)) return 0;
}
return 1;
}
python 实现拉宾米勒素数测试
有时需要判断一些极大的数是否为素数,因此需要用到python的大数类
import random
def QuickPow(a, b, MOD):
ret = 1
tmp = a%MOD
while b>0:
if (b&1):
ret = (ret*tmp)%MOD
tmp = (tmp*tmp)%MOD
b >>= 1
return ret
def Miller_Rabin(a, p): # a^(p-1) = 1 (mod p)
p1 = p-1
s2 = p1 & -p1 # fetch the last 1
x = QuickPow(a, p1//s2, p)
if x == 1 or x == p1:
return True
while s2>1:
x = (x*x)%p
if x == 1:
return False
if x == p1:
return True
s2 >>= 1
return False
def IsPrime(p, k):
if p == 2 or p == 3:
return True
if p < 2 or (p&1) == 0:
return False
for i in range(k):
if not Miller_Rabin(random.randint(2, p-1), p):
return False
return True
n = int(input())
for i in range(n):
p = int(input())
print('YES' if IsPrime(p, 5) else 'NO')
素数筛法
埃氏筛法
复杂度O(nloglogn)
bool prime[N];
void init(){
for(int i=2;i<N;i++) prime[i]=true;
for(int i=2;i<N;i++){
if(prime[i]){
for(int j=2*i;j<N;j+=i){
prime[j]=false;
}
}
}
}
线性筛法
bool prime[N];
int p[N],tot;
void init()
{
for(int i=2;i<N;i++) prime[i]=true;
for(int i=2;i<N;i++){
if(prime[i]) p[tot++]=i;
for(int j=0;j<tot&&i*p[j]<N;j++){
prime[i*p[j]]=false;
if(i%p[j]==0) break;
}
}
}