今天天气不错,我们开一节数论!
------------------------分割线------------------------
定义:若整数a和整数b除以正整数m的余数相等,则称a,b模m同余,记为a≡b(mod m).
同余类与剩余系:
对于∀a∈[0,m-1],集合{a+k*m}(k∈Z)的所有数模m同余,余数都是a.该集合称为一个模m的同余类,简记为a(符号百度百科)。
同余类
完全剩余系,
简化剩余系的定义
------------------------分割线------------------------
欧拉定理:
若正整数 a , n 互质,则 aφ(n)≡1(mod n) 其中 φ(n) 是欧拉函数(1~n) 与 n 互质的数。
证明如下:
不妨设X1,X2 … Xφn是1~n与n互质的数。
首先我们先来考虑一些数:aX1,aX2 … aXφn
这些数有如下两个性质:
(1)任意两个数模n余数一定不同:(反证)若存在aX1≡aX2(mod n),则 n |( aX1 - aX2 ),而a,n互质且(X1 - X2)< n,所以n不可能整除( aX1 - aX2 ),也就是说不存在aX1≡aX2(mod n)。归纳法:对于任意的与n互质的Xi均成立。故得证。
那么因为有 φn 个这样的数,Xi mod n(i=1~φn)所以就有 φn 个不同的余数,并且都是模数自然是(0~n-1)。
(2)对于任意的aXi(mod n)都与n互质。这不难想,因为a与n互质这是欧拉函数的条件,Xi是(1~n)与n互质的数的集合中的元素。所以如果aXi做为分子,n做为分母,那么他们构成的显然就是一个最简分数,也就是aXi和n互质。接下来就可以用欧几里得算法:因为:gcd(aXi,n)== 1 ,所以: gcd(aXi,n)== gcd(n,aXi%n)== 1
这样,我们把上面两个性质结合一下来说,aX1(mod n),aX2(mod n) … aXφn(mod n)构成了一个集合(性质1证明了所有元素的互异性),并且这些数是1~n与n互质的所有数构成的集合(性质1已说明)。这样,我们巧妙的发现了,集合{ aX1(mod n),aX2(mod n) … aXφn(mod n)}经过一定的排序后和集合{ X1,X2 … Xφn }完全一一对应。那么:aX1(mod n) aX2(mod n)* … * aXφn(mod n)= X1 * X2 * … * Xφn** 因此:我们可以写出以下式子:
aX1 * aX2 * … * aXφn ≡ X1 * X2 * … * Xφn (mod n),即:(aφn -1)X1 * X2 * … * Xφn ≡ 0 (mod n)
又因为X1 * X2 * … * Xφn与n互质,所以, (aφn -1)| n,那么aφn ≡ 1(mod n)。欧拉定理得证。
费马小定理:
对于质数p,任意整数a,均满足:ap≡a(mod p)
证明如下:
这个可以用欧拉定理来说明:首先,我们把这个式子做一个简单变换得:**ap-1 * a ≡ a(mod p) 因为a ≡ a(mod p)**恒成立,所以ap-1 mod p == 1时 费马小定理才成立,又因为p是质数,所以 φn == n-1 ,所以根据欧拉定理:若a,p互质则ap-1 mod p == 1成立。那么对于a,p不互质,因为p是质数,所以,a一定是倍数ap ≡ a ≡ 0(mod p)。综上所述,费马小定理成立,其实它算是欧拉定理的一个特例。
欧拉定理的推论:
若正整数a,n互质,那么对于任意正整数b,有ab≡ab mod φ(n)(mod n)
证明如下:(类似费马小定理的证明)
把目标式做一简单变形:ab - b mod φ(n)* ab mod φ(n)≡ ab mod φ(n)(mod n),所以接下来只需要证明ab - b mod φ(n)≡ 1 (mod n),又因为:( b - b mod φ(n))| φ(n),不妨设:( b - b mod φ(n))= qφ(n)(q为自然数),则有aqφ(n)== (aq)φ(n),因为a,n互质,那么(aq)与n也互质,那么就转换到了欧拉定理:(aq)φ(n)≡ 1 (mod n),成立。所以我们这个推论成立。
这个推论可以帮助我们在求幂运算的时候缩小数据范围和计算次数。具体的说:在求乘方运算时,可以先把底数对mod取模,再把指数对b mod φ(n)取模。
特别的,如果a,mod不互质,且b>φ(n)时,ab≡ab mod φ(n)+ φ(n)(mod n)。
------------------------分割线------------------------
扩展欧几里得算法:
Bezout算法:对任意整数a,b,存在一对整数x,y,满足ax+by=gcd(a,b).
证明:数学归纳法----略
代码:
int exgcd(int a,int b,int &x,int &y ){
if( b == 0){
x = 1, y = 0;
return a;
}
int d = exgcd(b,a%b,x,y);
int z = x; x = y;
y = z - y * (a / b);
return d;
}
------------------------分割线------------------------
乘法逆元:
若整数b,m互质,并且 b|a,则存在一个整数x,使得 a/b ≡ a* x(mod m),称为x 为 b 的模乘法逆元,记为b^-1(mod m).
证明:
因为a/b ≡ a b ^-1 ≡ a / b * b b ^ -1 (mod m),所以bb^-1≡ 1(mod m).
如果m 是质数(用符号p代替m),并且b < p,根据费马小定理,b ^(p-1) ≡ 1(mod p), 即为b * b^(p-2)≡1(mod p),因此,当模数为p为质数的时候,b ^(p-2)即为b的乘法逆元。
如果只是保证 b,m互质,那么乘法逆元可通过求解同余方程 bx ≡ 1(mod m)得到。
------------------------分割线------------------------
线性同余方程:
给整数a,b,m,求一个整数x满足 a * x ≡ b(mod m),或者无解。因为未知数的指数为1,所以我们称为一次同余方程,也称为线性同余方程。
ax ≡ b(mod m),等价于 a x -b是m的倍数,不妨设为- y倍,该方程可以改写为ax +my =b;
根据Bezout算法,方程有解当且仅当gcd(a,m)|b.
在有解的时候,先用欧几里得算法求出一组整数x0,y0,满足ax0+my0=gcd(a,m),然后x = x0 * b /gcd(a,m),就是原方程的一个解。
方程的通解就是所有模m/gcd(a,m)与x同余的整数。
中国剩余定理:
中国剩余定理
typedef long long ll;
ll china(ll a[],ll b[],int n)
{
ll M=1,y,x=0;
for(int i=0;i<n;++i)
M*=a[i];
for(int i=0;i<n;++i)
{
ll w=M/a[i];
ll tx=0;
int t=exgcd(w,a[i],tx,y);
x=(x+w*(b[i]/t)*x)%M;
}
return (x+M)%M;
}
------------------------分割线------------------------
高次同余方程:
问题:给定整数a,b,p,其中a,p互质,求一个非负整数p,使得 a ^ x ≡ b(mod p).
方案:
主要运用分块的思想;
将 x=im-j,其中m=ceil(sqrt(C));
A^(im-j)≡B(mod C);
A^(im / A^j) ≡ B(mod C);
A^(im) ≡ B * A^j(mod C);
枚举每个j(0<=j<=m),将B*A^j存入map;
再枚举每个i(0<=i<=m),判断A^(i *m)是否在map中存在值;
Baby Step,Giant Step算法;------BSGS(拔山盖世)
#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
long long num=0;
char c=' ';
bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
return flag ? num : -num;
}
int A,B,C;
namespace ksm
{
long long qpow(long long a,long long t,long long p)
{
int base=a%p;
long long ans=1;
while(t)
{
if(t&1)
ans=(long long)ans%p*(base%p)%p;
base=(long long)base%p*(base%p)%p;
t>>=1;
}
return ans;
}
}
namespace BSGS
{
map<long long,int>mp;
int bsgs(int A,int B,int C)
{
mp.clear();
if(A%C==0)
return -1;
int p=false;
int m=ceil(sqrt(C));
long long ans;
for(int i=0;i<=m;i++)
{
if(!i)
{
ans=B%C;
mp[ans]=i;
continue;
}
ans=ans*A%C;
mp[ans]=i;
}
long long t=ksm::qpow(A,m,C);ans=1;
for(int i=1;i<=m;i++)
{
ans=(ans*t)%C;
if(mp[ans])
{
int t=i*m-mp[ans];
return (t%C+C)%C;
p=true;
break;
}
}
if(!p)return -1;
}
}
int main()
{
int t=read();
int l=read();
if(l==1)
{
using namespace ksm;
while(t--)
{
int a=read();
int c=read();
int b=read();
printf("%lld\n",qpow(a,c,b));
}
return 0;
}
if(l==2)
{
using namespace ksm;
while(t--)
{
int y=read();
int z=read();
int p=read();
int gcd=__gcd(y,p);
if(z%gcd)
{
printf("Orz, I cannot find x!\n");
continue;
}
printf("%d\n",((z%p)*(long long)qpow(y,p-2,p))%p);
}
}
if(l==3)
{
using namespace BSGS;
using namespace ksm;
while(t--)
{
A=read();B=read();C=read();
long long ans=bsgs(A,B,C);
if(ans==-1)
{
printf("Orz, I cannot find x!\n");
continue;
}
printf("%lld\n",ans);
}
}
return 0;
}