一、同余
结论
(a + b ) m o d n = ( ( a m o d n ) + ( b m o d n ) ) m o d n
( a − b ) m o d n = ( ( a m o d n ) − ( b m o d n ) ) m o d
( a × b ) m o d n = ( ( a m o d n ) × ( b m o d n ) ) m o d
二、GCD&LCM
GCD
GCD即最大公约数,小学的时候我们就学习了求两数的最大公约数的方法。但是要注意如果有一个数为0的话那么最小公约数就是另一个不为0的数,我们在这里只需要知道GCD是什么东西就行了
更相减损法
两个正整数a和b(a>b),它们的最大公约数等于a-b的差值c和较小数b的最大公约数。
int gcd_3(int a,int b) {//更相减损法 递归写法
if(a == 0)
return b;
if(b == 0)
return a;
if(a == b)
return a;
if(a > b)
return gcd_3(a-b,b);
if(a < b)
return gcd_3(b-a,a);
}
int gcd_4(int a,int b) {//更相减损法 循环写法
if(a == 0)
return b;
if(b == 0)
return a;
while(a != b) {
if(a > b)
a -= b;
else
{
int t = a;
a = b - a;
b = t;
}
}
return a;
}
辗转相除法
两个正整数a和b(a>b),它们的最大公约数等于a除以b的余数c和b之间的最大公约数。
其实就是把更相减损变得更高级一点(加减运算变乘除运算,提升了一个级别)
但是大整数取模会让一些题极为头疼,所以我们还是要慎重考虑什么时候用更相减损什么时候用辗转相除。
int gcd_1(int a,int b){//辗转相除法 循环写法
while(b > 0) {
int t = a%b;
a = b;
b = t;
}
return a;
}
int gcd_2(int a,int b) {//辗转相除法 递归写法
return b?gcd_2(b,a%b) : a;
}
LCM
LCM即最小公倍数,小学的时候也学过,当我们求出GCD的时候,LCM也就出来了,
L C M = a × b /G C D ( a , b )
拓展欧几里得
前置知识
辗转相除法、贝祖定理
辗转相除法就是不断的让较大的数模上较小的数,最后使得其中一个数位0,最后得到答案
贝祖定理:$ax+by=c,x∈Z*,y∈Z* \ 成 立 的 充 要 条 件 是 成立的充要条件是成立的充要条件是gcd(a,b)|c$
贝祖定理证明:
设s = g c d ( a , b ) s=gcd(a,b)s=gcd(a,b),显然s ∣ a ,并且s ∣ b
又因为x , y ∈ Z
所以s ∣ (a* x),s ∣ ( b* y )
显然要使得之前的式子成立,则必须满足c cc是a aa和b bb的公约数的倍数
又因为x 和y 是正整数
所以c 必然是a , b 最大公约数的倍数。
因此,证得该定理成立
当然我这里写的可能只有两元,但是这个定理可以拓展到n元,请大家思考拓展到n元的情况
问题引出
求得任意一组解:a* x + b * y = 1
思路
很显然,我们能知道如果g c d ( a , b ) ! = 1 的话是无解的,所以我们只看g c d ( a , b ) = 1 的情况,通过拓展欧几里得的算法我们可以解决这一类问题
拓展欧几里得算法代码
int ex_gcd(int a,int b,int &x,int &y)//返回的值还是GCD(a,b)
{
if(b==0)//等于0的情况直接返回了
{
x=1;
y=0;
return a;
}
int ans=ex_gcd(b,a%b,x,y);//获得x',y';
int temp=x;//存储x'
x=y;//x=y'
y=temp-(a/b)*x;//y=x'-(a/b)*y'
return ans;
}
这里注意一下:函数在读取 x 和 y 的时候使用的是 & ,
&是引用符号,与普通的函数调用不同,这个引用可以直接改变x , y 的值,而且不会消失,普通的函数调用都是使用临时变量。
2.3.5 原理证明
若a x + b y = c 有解,且设t = g c d ( a , b ) ,则c % t = 0
①设a x + b y = t
当b等于0时,t = a
因为g c d ( a , 0 ) = a ,则会有a × x = a ,即x = 1
② 当b不等于0时
设a x + b y = g c d ( a , b ) —Ⅰ
我们可以推出下一层的状态:( a , b 用到了辗转相除法)
b x ′ + ( a % b ) y ′ = g c d ( b , a % b ) — Ⅱ
又因为 g c d ( b , a % b ) = g c d ( a , b ) — Ⅲ
所以由Ⅰ、Ⅱ、Ⅲ可得:a x + b y = b x ′ + ( a % b ) y ′ —Ⅴ
又因为a % b = a − ⌊ a b ⌋ × b — Ⅵ
所以由Ⅴ、Ⅵ可得:a x + b y = a ∗ y ′ + b × ( x ′ − ⌊ a /b ⌋ × y ′ )
所以可以得到:x = y ′ , y = x ′ − ⌊ a /b ⌋ × y ′
拓展gcd模板(求ax+by=c的任意一组解):
由贝祖定理我们可以得到a x + b y = k ∗ g c d ( a , b ) 一定存在
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){
x = 1,y = 0;
return a;
}
ll d = exgcd(b,a%b,y,x);
y -= a/b * x;
return d;
}
int main()
{
int n;
scanf("%d",&n);
while(n--){
ll a,b,x,y;
scanf("%lld %lld",&a,&b);
exgcd(a,b,x,y);
printf("%lld %lld\n",x,y);
}
return 0;
}
原文链接:
https://blog.csdn.net/m0_46201544/article/details/122896816
https://blog.csdn.net/m0_46201544/article/details/122280910