扩展欧几里得与逆元
例:计算gcd(210,715)
715=3*2+85
210=2*85+40
85=2*40+5
40=8*5+0(到0就可以停下了)
所以gcd(210,715)=5
(从下往上推)
5=1*85+2*40=1*85-2*(210-2*85)=-2*210+5*85=-2*210+5*(715-3*210)=-17*210+5*715
最终可以得到5=-17*210+5*715
5是210和715的最大公约数,所以5=210x+715y(*)
一个方程中有两个未知数(*)形式时可利用欧几里得求解x和y
Ex_gcd的代码实现
需要把模板背下来
void Ex_gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;y=0;return;
}
Ex_gcd(b,a%b,x,y);
int t;
t=x;
x=y;
y=t-(a/b)*y;
}
在扩展欧几里得算法中,递归的出口条件(即基本情况)是当 b 等于 0 时。此时,a 和 b 的最大公约数(GCD)就是 a 本身。因此,在这种情况下,方程 ax + by = gcd(a, b) 变为 ax + 0y = a。
这个方程的解很简单:x 可以是 1(因为 a 乘以 1 等于 a),而 y 可以是 0(因为 0 乘以任何数都是 0)。这就是递归出口的条件:当 b 为 0 时,x 被设置为 1,y 被设置为 0,然后函数返回。
这个递归出口是扩展欧几里得算法能够正确工作的关键之一。每次递归调用都会减小 b 的值,直到 b 变为 0,此时算法找到了一个解(x 和 y 的值)。然后,算法利用这个解和扩展欧几里得算法的性质来“回溯”并找到原始方程 ax + by = gcd(a, b) 的解。
简而言之,当 b 为 0 时,我们知道 a 和 b 的最大公约数是 a,因此方程 ax + by = a 的解是 x = 1, y = 0。这就是递归出口的作用。
Ex_gcd计算乘法逆元
逆元的定义
ax1(mod m),我们将x叫做a在模c下的逆,也就是逆元,可以记作
用途
在处理%c时,取模运算对除法是没有分配律的(原因是先取模不能保证余数能整除),我们可以先求出b的逆元,这里的(mod m)
这样就把原来的除法取模转化成乘法了
条件
a和b必须互素,即gcd(a,b)=1
例题
林大oj 1748 同余方程
Description
求关于 x的同余方程 ax≡1(mod b) 的最小正整数解。
Input
一行,包含两个正整数 a,b用一个空格隔开。
Output
一个正整数 x ,即最小正整数解。输入数据保证一定有解。
Sample Input
3 10
Sample Output
7
#include <bits/stdc++.h>
using namespace std;
void Ex_gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return;
}
Ex_gcd(b,a%b,x,y);
int t;
t=x;
x=y;
y=t-(a/b)*y;
}
int main()
{
int a,b,x,y;
cin>>a>>b;
Ex_gcd(a,b,x,y);
if(x<0)
x=(x+b)%b;
cout<<x<<endl;
return 0;
}
注:本题要求最小正整数解,计算出x是负数的话,要变成正数,ax+by=1,等价于ax=1(mod b);
是对b取余,所以要(x+b)%b
林大oj 519 昨日重现
f(n)=1^2+2^2+3^2+.......+n^2
计算f(n)对1007取余的值
Input
输入数据有多组(不超过100组),每组一个数n. (1<=n <=1,000,000,000).
Output
输出f(n)对1007取余的值。
Sample Input
3
4
100
Sample Output
14
30
1005
f(n)=(n*(n+1)*(2*n+1))%1007
处理:6x+1007y=1等价于6x≡1(1007)
求6的逆
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void Ex_gcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return;
}
Ex_gcd(b,a%b,x,y);
int t;
t=x;
x=y;
y=t-(a/b)*y;
}
int main()
{
ll a,b,x,y,n,ans;
Ex_gcd(6,1007,x,y);
x=(x+1007)%1007;
while(cin>>n)
{
ans=(n*(n+1))%1007;
ans=(ans*(2*n+1))%1007;
ans=(ans*x)%1007;//其实x=168
cout<<ans<<endl;
}
return 0;
}
这里还运用了公式(a*b*c)%m=((a%m)*(b%m)*(c%m))%m
当然,你也可以直接手算6的逆
1007=167*6+5
6=1*5+1
1=6-1*5=6-1*(1007-167*6)=168*6-1*1007
即6的逆等于168
只要求(168*n*(n+1)*(2*n+1))%1007即可