Ex_gcd(扩展欧几里得)

扩展欧几里得与逆元

例:计算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计算乘法逆元

逆元的定义

ax\equiv1(mod m),我们将x叫做a在模c下的逆,也就是逆元,可以记作a^{-1}

用途

在处理\frac{a}{b}%c时,取模运算对除法是没有分配律的(原因是先取模不能保证余数能整除),我们可以先求出b的逆元b^{-1},这里的\frac{a}{b}\equiv a b^{-1}(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)=\frac{1}{6}(n*(n+1)*(2*n+1))%1007

\frac{1}{6} 处理:6x+1007y=1等价于6x≡1(1007)

求6的逆6^{-1}

#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即可

  • 0
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值