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

这里总结了一些基础的数学概念。

整除:

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

带余数除法:

a、b为两个正整数,且b≠,0,则存在唯一整数q和r,使得a=qb+r,0<=r

关于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


③   求解模的逆元

当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


 

 

欧拉函数

对正整数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)

 

 

你可能感兴趣的:(算法)