您的位置:首页 > 其它

【数论】gcd|扩展gcd|素数筛法|快速幂|欧拉函数(各种模板)

2017-03-15 14:08 435 查看
这里总结了一些基础的数学概念。

整除:

一个数可以被另一个数整除是数论中的一个关键概念。d|a(d整除a,a除以d)的含义是存在某个整数k,使得a=kd。称a是d的倍数,d是a的约数(因数)。

带余数除法:

a、b为两个正整数,且b≠,0,则存在唯一整数q和r,使得a=qb+r,0<=r<b,则有r=a mod b。例如10 mod 2=0,7 mod 3=1。可以用来判断一个正数是奇数还是偶数,如当n%2得到的余数是0,则n为偶数,如果n%2得到的余数是1,则n为奇数。

关于d|b的一些推论

①   d|a且d|b,则d|(a+b)且d|(a-b)

②   d|a且d|b,则d|(ax+by)

③   a|b且b|a,则a=±b

最大公约数:

一般用gcd(a,b)来表示a和b的最大公约数。并且对于任意非负整数a和任意正整数b,有gcd(a,b)=gcd(b,a mod b),证明略。

用代码求最大公约数:

int gcd(int a,int b)//保证a>b
{
return b?gcd(b,a%b):a;
}


关于最大公约数的一些推论:

①   对于ax+by(x,y∈Z)的线性组合集,gcd(a,b)是这个集合中的最小正整数。

②   d|a且d|b,则d|gcd(a,b)

③   对于n,a,b∈N+,如果n|ab,且gcd(a,n)=1,则n|b

互质数:

对于两个整数a和b,如果gcd(a,b)=1,则称a和b为互质数。等价于ax+by=1

唯一因子分解定理:

合数a仅能以一种方式写成如下乘积形式a=p1^e1*p2^e2*p3^e3……*pn^en,其中pi为素数,ei均为正整数。

扩展gcd算法:

该算法用来求解形如ax+by=c的通解。我们知道了ax+by=gcd(a,b)的方程肯定存在解,只要我们找到一组特殊的解x0和y0,便可以通过x=x0+(b/gcd(a,b))*t,y=y0-(a/gcd(a,b))*t。

考虑当前状态和下一个状态,有ax+by=gcd(a,b)=gcd(b,a%b)=bx1+(a%b)y1=bx1+(a-(a/b)*b)y1=ay1+b(x1-a/b*y1)。

前后式子对比,可得x=y1,y=x1-a/b*y1;

int e_gcd(int a,int b,int &x,int&y)
{
if(b==0){x=1;y=0;return a;}
int ans=e_gcd(b,a%b,x,y);
int temp=x;
x=y;
y=temp-a/b*y;
return ans;
}


扩展gcd的三个应用:

①   求解不定方程

形如ax+by=c的不定方程,若c mod gcd(a,b)=0,则该方程存在整数解,否则不存在。

 

②   求解模线性方程(线性同余方程)

同余方程ax≡b(mod n)(即ax和b关于n同余 b)对于未知数x有解,当且仅当gcd(a,n)|b。且当方程有解时,方程有gcd(a,n)个解。

对于该同余方程,相当于求解ax+ny=b。

证明:设d=gcd(a,n),则ax+ny=d.如果d|b,则ax0+ny0=d,同乘b/d得ax0*b/d+ny0*b/d=b。所以x=x0*b/d,y=y0*b/d为ax+ny=d的一个解,即x=x0*b/d为ax≡b(mod n)的解。且方程的 d个解分别为 xi= (x0+ i* (n/ d ))mod n {i= 0... d-1}。

设ans=x*(b/d),s=n/d;方程ax≡b (mod n)的最小整数解为:(ans%s+s)%s;

boolmodular_linear_equation(int a,int b,int n)
{
int x,y,x0,i;
int d=exgcd(a,n,x,y);
if(b%d)
return false;
x0=x*(b/d)%n;   //特解
for(i=1;i<d;i++)
printf("%d\n",(x0+i*(n/d))%n);
return true;
}


③   求解模的逆元

当ax≡b(mod n)的gcd(a,n)=1时,方程只有唯一解。如果b=1,则x是a对模n的乘法逆元。即对于任意ax≡1(mod n),如果gcd(a,n)=1,那么方程ax≡1(mod n)对模n有唯一解,否则无解。解方程组ax+ny=1,求得x即为逆元。

int cal(int a,int m)
{
intx,y;
intgcd=e_gcd(a,m,x,y);
if(1%gcd!=0)return -1;
x*=1/gcd;
m=abs(m);
inans=x%m;
if(ans<0)ans+=m;
returnans;
}


中国剩余定理:

有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?翻译过来就是有一个数x,x%3=2,x%5=3,x%7=2,求x。中国剩余定理便用来解决这一类问题。

(设m1,m2,···,mk两两互素)

x=a1(mod m1);

x=a2(mod m2);

···

x=ak(mod mk);

其在M=m1*m2*···*mk;中有唯一整数解。

记Mi=M/mi;因为gcd(Mi,mi)=1,故有两整数pi,qi满足Mi*pi+mi*qi=1,如果记ei=Mi*pi;那么:ei=0 (mod mj),j!=i;ei=1(mod mj),j=i;

很明显,e1*a1+e2*a2+···+ek*ak就是方程的一个解,加减M倍后就可以得到最小非负整数解了。

如果m1,m2,···,mk不互素,那只能两个两个求了。

x=a1 (mod m1);

x=a2 (mod m2);

解完后,a=x; m=m1和m2的最小公倍数。即可。

以下代码中a数组是除数,b数组是余数

int main()
{
double a[8], b[8], c[8], flag, max;
int i, k;
double  answer = 0, plus = 1, j;
for(i = 0; i < 8; i ++)
{
scanf("%lf", &a[i]);
plus *=a[i];
}
for(i = 0; i < 8; i ++)
scanf("%lf", &b[i]);
max = a[0];
for(i = 0; i < 8;i ++)
{
max = max < a[i]? a[i]:max;
for(j = 1; ; j ++)
{
flag=j * plus/a[i];
if(fmod(flag , a[i]) == b[i]) break;//fmod(x,y)计算x/y的余数,返回x-n*y,用来计算浮点类型的
}
c[i] = flag;
answer += c[i];
}

j=fmod(answer, plus);
if(j < max) j +=plus;
printf("%.0lf\n", j);
return 0;
}


 

 

素数筛法:Eratosthenes 筛选

for(int i=2;i<=n;i++)
is_prime[i]=1;
for(int i=2;i<=n;i++)
{
if(is_prime[i])
for(intj=i*I;i<=n;i++)
is_prime[j]=0;
}


因式分解:将数分解为唯一因子分解定理中的形式。

long long factor[100][2];
int facCnt;
int getFactors(long long x)//把x进行素数分解
{
facCnt=0;
long long tmp=x;
for(int i=1;prime[i]<=tmp/prime[i];i++)
{
factor[facCnt][1]=0;
if(tmp%prime[i]==0)
{
factor[facCnt][0]=prime[i];
while(tmp%prime[i]==0)
{
factor[facCnt][1]++;
tmp/=prime[i];
}
facCnt++;
}
}
if(tmp!=1)
{
factor[facCnt][0]=tmp;
factor[facCnt++][1]=1;
}
return facCnt;
}


 

二分快速幂:

求解a的b次方,我们可以通过将b看成二进制来加速a的b次方的求解,比如a的17次方,可以看成是a^1*a^16,而a^16=a^8*2=a^4*2*2=a^2*2*2*2=a*2*2*2*2,只需要5次运算。通常计算a^b伴随着取模,即a^b%c,代码如下

int pow_mod(int a,int b,int mod)
{
intres=1,temp=a;
for(;b;b/=2)
{
if(b&1)res=res*temp%mod;//二进制上这一位为1,乘上这一位权值
temp=temp*temp%mod;
}
return res;
}


排列组合:

排列:从n个不同元素中取出m个的排列数。A(n,m)=n!/(n-m)!

组合:从n个不同元素中取出m个的组合数。C(n,m)=A(n,m)/m!=(n!)/(m!(n-m)!)

组合数的性质

①   C(n,m)=C(n,n-m),从n个里面选m个的方案数和从n个里面选n-m个的方案数相同。

②   C(n,m)=C(n-1,m)+C(n-1,m-1),从n个里面选m个的方案等于从n-1个里面选出m个的方案(不选第n个)加上从n-1个里面选出m-1个的方案(选第n个)。

可以发现性质2刚好是杨辉三角(递推式为F[i][j]=F[i-1][j]+F[i-1][j-1]),因此我们可以通过这种方式求出组合数。

 

矩阵快速幂:快速求矩阵的n次方

struct matrix{
intn,m;
lla[5][5];
};
matrix matrix_mul(matrix A,matrix B,intmod)
{
matrixC;
C.n=A.n;
C.m=B.m;
for(inti=0;i<A.n;i++)
{
for(intj=0;j<B.m;j++)
{
C.a[i][j]=0;
for(intk=0;k<A.m;k++)
{
C.a[i][j]+=A.a[i][k]*B.a[k][j]%mod;
C.a[i][j]%=mod;
}
}
}
return C;
}
matrix unit()
{
matrix res;
res.n=2;
res.m=2;
for(inti=0;i<res.n;i++)
{
for(intj=0;j<res.m;j++)
{
if(i==j)
res.a[i][j]=1;
else
res.a[i][j]=0;
}
}
return res;
}
matrix matrix_pow(matrix A,ll n,ll mod)
{
matrix res=unit(),temp=A;
for(;n;n/=2)
{
if(n&1)res=matrix_mul(res,temp,mod);
temp=matrix_mul(temp,temp,mod);
}
return res;
}


 

 

欧拉函数

对正整数n,欧拉函数是少于或等于n的数中与n互质的数的数目。例如euler(8)=4,因为1,3,5,7均和8互质。

     Euler函数表达通式:euler(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…(1-1/pn),其中p1,p2……pn为x的所有素因数,x是不为0的整数。euler(1)=1(唯一和1互质的数就是1本身)。 

     欧拉公式的延伸:一个数的所有质因子之和是euler(n)*n/2。

//直接求解欧拉函数

int euler(int n){ //返回euler(n)
int res=n,a=n;
for(int i=2;i*i<=a;i++){
if(a%i==0){
res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出
while(a%i==0) a/=i;
}
}
if(a>1) res=res/a*(a-1);
return res;
}


筛选法求欧拉函数,时间复杂度O(nloglogn)

void init()
{
int i, j;
memset(phi, 0, sizeof(phi));
phi[1] = 1;
for(int i = 2; i < SIZE; i++) if(!phi[i])
{
for(j = i; j < SIZE; j+=i)
{
if(!phi[j]) phi[j] = j;
phi[j] = phi[j] / i * (i-1);
}
}
return ;
}


欧拉定理:

欧拉定理表明,若n,a为正整数,且n,a互质,(a,n) = 1,则a^φ(n) ≡ 1 (mod n) 

费马小定理:

假如p是质数,且a,p互质,那么 a的(p-1)次方除以p的余数恒等于1 。gdc(a,p)=1,那么a^(p-1) ≡1(mod p)

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: