<福州集训之旅Day5> | 数论 |

可怕的数论

gcd与lcm
同余,整除与不定方程
拓展欧几里得
逆元
费马小定理
排列组合

<更新提示>

<第一次更新>丧心病狂的数论开讲啦!
<第二次更新>更正了几个错别字,顺便更新了在码字过程中“拓展欧几里得”算法的通解x_,y_与代码x1,y1不一致,容易导致误解的问题,还修正了“费马小定理求解逆元”中的推导过程漏字的问题。
<第三次更新>更正了“费马小定理求解逆元”推导过程中a(p-1)未改为a(p-2)的问题。补充说明了“费马小定理求解逆元”。


<正文>

gcd(最大公因数)与lcm(最小公倍数)之间的关系

众所周知地,求gcd最著名的方法就是辗转相除法,这边为大家附上辗转相除法的代码

int gcd(int a,int b)
{returnb==0?a:gcd(b,a%b);}

利用两数之间奇特的余数交换关系,就能求出两数的最大公因数。对于gcd的证明过程,在这边也简单的提一下:
辗转相除法的证明

  设两数为a、b(b<a),求它们最大公约数的步骤如下:用b除a,即a/b=q…..r,得a=bq+r(0≤r<b,即为余数)(q是这个除法的商)。若r=0,则b是a和b的最大公约数,a,b存在倍数关系。若r≠0,则继续考虑。

  首先,应该明白的一点是任何 a 和 b 的公约数都是 r 的公约数。要想证明这一点,就要考虑把 r 写成 r=a-bq。现在,如果 a 和 b 有一个公约数 d,而且设 a=sd , b=td, 那么 r = sd-tdq = (s-tq)d。因为这个式子中,所有的数(包括 s-tq )都为整数,所以 r 可以被 d 整除。

  对于所有的 d 的值,这都是正确的;所以 a 和 b 的最大公约数也是 b(因为b< a,所有取b继续运算才能不断缩小规模,直至两数有倍数关系) 和 r 的最大公约数。因此我们可以继续对 b 和 r 进行上述取余的运算。这个过程在有限的重复后,可以最终得到 r=0 的结果,我们也就得到了 a 和 b 的最大公约数

对此,我们又可以根据gcd和lcm之间最基本的关系,求出lcm。即lcm=ab/gcd,我们也能写出lcm的求值函数

int lcm(int a,int b)
{
    return a*b/gcd(a,b);
}

当然,gcd(a,b)*lcm(a,b)=ab也显然是成立的。
但是我们今天要提的不只是辗转相除法,我们反而应该认识gcd与lcm的朴素算法。

设有

a=pa11pa22...pakk

b=pb11pb22...pbkk

因为a,b因子底数不一定完全相同,所以允许指数为零。有了a,b的因式表达式,自然就能推出gcd,lcm的表达式,即为gcd,lcm的朴素算法:

gcdab=pmin(a1b1)1pmin(a2b2)2...pmin(akbk)k

lcm同理可推得:
lcmab=pmax(a1b1)1pmax(a2b2)2...pmax(akbk)k

经过思考,这显然是成立的,我们就又得到了一种求gcd和lcm的算法。

同余,整除与不定方程

在讲解之前,先定义a≡b(modm),其意即为a与b对m取余相等,也能表达为a%m=b%m,当然m|a-b(|为整除号,意为(a-b)%m=0,后面被前面整除)一定成立,定义完了符号,就可以开始同余之旅了。

同余的性质:
(1)对于同一个除数,两数的和(或差)于他们余数的和(或差)同余数。
即可表达为:a1≡a2 (mod m),b1=b2时, a1±b1≡a2±b2(mod m)。
(2)对于同一个除数,两数的乘积与他们余数的乘积同余。
即可表达为:a≡b(mod m)时,ab≡[(a%m)(b%m)](mod m)。
(3)对于同一个除数,如果两个整数同余,那么他们的差就一定能被这个数整除。
即可表达为:a≡b(mod m)时,m|a-b(即定义符号时的表达式)。
(4)对于同一个除数,如果两个整数同余,那么他们的乘方仍然同余。
即可表达为:a≡b(mod m)时,a2≡b2(mod m)。

这是关于同余的定理,在数论同余的题目中重要的时灵活应用性质,才能快速的解题。

不定方程常用小定理:
若gcd(a,b)=d,则方程ax+by=d一定有至少一组x,y整数解。同理,也能逆向推导,如果ax+by=d有整数解,则必然有gcd(a,b)=d。再逆向运用性质1,又可得d|ax,d|by。这里,我们就要向下讲解了。

扩展欧几里得

扩展欧几里得是一种求解方程ax+by=c的一组通解x1,y1的算法,使他们满ax+by=gcd(a,b)=d(贝祖等式)。具体的是利用辗转相除法和同余的性质,使ax+by=gcd(a,b)=gcd(b,a%b)=…,利用辗转相除法缩小a,b的规模,直至b=0,这时,我们再使x=1,y=0,这样原方程一定成立。我们就可以从最终状态倒推x,y。
具体过程如下:
a*x+b*y = gcd(a , b)
= gcd(b , a mod b)
我们设有x1,y1,使得下等式成立:
= b * x1 + (a mod b) * y1
= b * x1 + (a - a / b * b) * y1
即a*x+b*y = b * x1 + (a - a / b * b) * y1
化简上式,得a*x+b*y = a*y1 - b*a/b*y1 + b*x1 ,
即a * x + b * y= a * y1 + b * (x1-a/b*y1)
所以有:x=y1,y=x1 - a/b*y1
如果这样理解了,那代码不就显而易见了吗?

#include<bits/stdc++.h>
using namespace std;
int exgcd(int &x,int &y,int a,int b)
{
    if(b==0){x=1,y=0;return a;}
    int x1,y1,g;
    g=exgcd(x1,y1,b,a%b);
    x=y1;y=x1-(a/b)*y1;
    return g;
}
int main()
{
    int a,b,x,y;
    cin>>a>>b;
    int d=exgcd(x,y,a,b);
    cout<<"x:"<<x<<" "<<"y:"<<y<<" "<<"d:"<<d<<endl;
    return 0; 
}

如果实在不理解,再这里推荐一篇博客吧——exgcd.
扩展欧几里得的算法就是如上的,相比普通欧几里得,拓展欧几里得求了x,y的一组通解显然,这求出的是所有通解的特解,再这之中比如有一个数对于另一个数的乘法逆元。

逆元

什么是乘法逆元?当ab≡ 1(mod m)时,我们称a,b再mod的意义下互为对方的乘法逆元,或者说x 是 a 关于 m 的乘法逆元。我们记a=b-1,b=a-1。(b-1*b≡b-1+1≡b0≡1)。
例:
4*4≡1(mod 5),即再mod 5的意义下,4和其本身互为逆元。我们仍然记4=4-1。(不要对4=1/4感到奇怪,再乘法逆元中,确实时这样定义的,只需要意会,理解即可)事实上,逆元确实也称为数论倒数。接下来我们开始讲逆元存在的意义。
先看同余的加减乘除运算:
(a + b)≡(a%p + b%p)(mod p)(对)
(a - b)≡ (a%p - b%p)(mod p)(对)
(a * b)≡(a%p * b%p) (mod p)(对)
(a / b)≡(a%p / b%p) (mod p)(错)
证明对是很难的,但证明错举一个反例即可:
(100/50)%20 = 2 ≠ (100%20) / (50%20) %20 = 0
对于一些大数据题目,我们必须再计算过程中对结果进行不断求余,但如果中间的运算是乘法,那是不是取余就不行了呢?
答案是否定的,这时候就要用到乘法逆元了。
我们知道,如果x为a的倒数,则x=1/a。但现在问题变了,我们要使x让a*x ≡1 (mod p)成立,那x一定等于1/a吗?不是的,所有我们就要把x称为a关于p的逆元了。例如 2*3≡1(mod p)依然成立,但2不是3的实数意义上的倒数,3也不是2在实数意义上的倒数,但他们在模意义上是成立的。所以逆元将除法转换为了乘法,这便是逆元的意义了。

同时这里给出几个根据逆元得出的结论:
ax≡1(mod m)
1)m|ax+1
2)ax-1=my(y是一个整数)
3)ax-my=1
4)gcd(a,m)|1(即若a在mod m情况下有逆元,则a与m互质)

接下来步入正题——求解逆元:

还记得扩展欧几里得算法吗?
在扩展欧几里得中,通常的等式为:a*x+b*y = gcd(a , b),但是扩展欧几里得有一种特殊的情况:当gcd(a,b)=1时,a*x+b*y=1,显然在这种情况下a,b时互质的,如果x,y有解,那x就是a关于b的逆元,y就是b关于a的逆元。
推理过程如下:
a*x + b*y = 1
将两边同时求余b
a*x % b + b*y % b = 1 % b
a*x % b = 1 % b
a*x ≡1 (mod b)
ax≡1(mod b)出现了!
所以x是a关于b的逆元
同理可证明y是b关于a的逆元
上代码!

long long inv(int a,int b)//我们记inv(a,b)为a关于b的逆元
{
    long long x,y;
    int d=exgcd(x,y,a,b);
    return d == 1 ? (x % b + b) % b : -1;

}

接下来,我们用费马小定理来求逆元!

费马小定理

费马小定理是数论中极其重要的定理,它的说明如下:
假设gcd(a,p)=1,那么a(p-1) ≡1 (mod p)
如果费马小定理是成立的(废话),那么我们可以用其推出a的逆元,过程如下:
a(p-1) ≡1 (mod p)
两边同时除以a
a(p-2) ≡1/a (mod p)
不对,数论中不能用1/a
a(p-2) ≡inv(a)(mod p)
所以inv(a)≡a(p-2)(mod p)
在这里inv(a)=a(p-2)时条件显然成立,我们计算a(p-2)即为a在模p意义下的一个逆元。
这个用快速幂求一下,复杂度O(logn)

int quickpower(int a,int b,int c) //a的b次方模c 
{  
    int ans=1;   //记录结果  
    a=a%c;   //预处理,使得a处于c的数据范围之下  
    while(b!=0)  
    {  
        if(b&1) ans=(ans*a)%c;   //如果b的二进制位不是0,那么我们的结果是要参与运算的  
        b>>=1;    //二进制的移位操作,相当于每次除以2,用二进制看,就是我们不断的遍历b的二进制位  
        a=(a*a)%c;   //不断的加倍  
    }  
    return ans;  
}  
long long fermat(long long a,long long p)
{
    return quickpower(a,p-2,p);
}

好吧,接下来剩的就是证明费马小定理了。有兴趣自己参考吧!
第一步:
证明引理:
设m是一个整数且m>1,b是一个整数且gcd(m,b)=1。如果a[1],a[2],a[3],a[4],…a[m]是模m的一个完全剩余系,则b·a[1],b·a[2],b·a[3],b·a[4],…b·a[m]也构成模m的一个完全剩余系
(完全剩余系:从模n的每个剩余类中各取一个数,得到一个由0到n-1,n个数组成的集合,叫做模n的一个完全剩余系。)
证明:用反证法,若存在2个整数b·a[i]和b·a[j]同余即b·a[i]≡b·a[j](mod m)..(i>=1 && j>=1),根据同余定理则有a[i]≡a[j](mod m)。根据完全剩余系的定义可知这是不可能的,因此不存在2个整数b·a[i]和b·a[j]同余。

证完了引理,我们开始证费马小定理:
构造p的完全剩余系:

P=(1,2,3...p1)

根据已证的引理,我们得知
(a,2a,3a...a(p1))
也是p的完全剩余系。
根据完全剩余系的性质显然可得:
(123...p1)(a2a3a...a(p1))(modp)

提取公因式,得:
(p1)!(p1)!ap1(modp)

根据同余的性质,两边同时约去(p-1)!,得:
1ap1(modp)
是不是似曾相识,费马小定理证毕。

排列组合

排列组合其实和数论没什么关系啦,在这里简单提一提吧!
排列组合分A和C两种,A(n,m)(n为下标,m为上标)意为从1到n中选m个数进行排列的方案数,C(n,m)意为从1到n中选m个数的方案数,不计排列。计算公式如下:

Amn=n!(nm)!

Cmn=n!m!(nm)!

对于排列组合的计算公式,还有几个另外的拓展公式:

Cnm=Cn1m+Cn1m1

Cn0+Cn1+...+Cnn=2n

Cnm=cnnm

Cnm=nmCn1m1

对于二项展开式定理,排列组合也有相应的公式:
(a+b)n=i=0nCniaibn1

数论内容部分就告一段落啦!


<后记>
有人给我评价回复一下吗?码字超辛苦的,指出错误也行啊。


<废话>

猜你喜欢

转载自blog.csdn.net/prasnip_/article/details/79350712
今日推荐