zoukankan      html  css  js  c++  java
  • exgcd复习 Apare_xzc

    exgcd复习

    2019/3/25 xzc


    拓展欧几里得原理可以用来解这样的不定方程:
    aX + bY = c(求X,Y)


    若c不是gcd(a,b)的倍数,则该不定方程一定无解
    则问题可以转化为求不定方程的一组解{x,y}:
    aX + by = gcd(a,b)


    我们知道欧几里得算法:gcd(a,b) = gcd(b,a%b)
    (1.0). a * X1 + b * Y1 = gcd(a,b)
    (2,1). b * X2 + a%b*Y2 = gcd(b,a%b)
    (2.2). b*X2 + (a-a/b*b)*Y2 = gcd(b,a%b)

    联立(1.0)和(2.2)可得:
    a * X1 + b * Y1 = b * X2 + (a-a/b*b)*Y2
    化简后,用待定系数法:
    a * X1 + b * Y1 = a * Y2 + b * (X2 - a/b*Y2)
    可得 :X1 = Y2 , Y1 = (X2 - a/b*Y2)
    如果递归下去,b==0,则a = gcd
    此时方程为:aX+bY = a 则可令x = 1, y = 0
    那么要求原始方程的X,Y,我们要回溯回去,即用X2和Y2推回去X1和Y1
    令X_1 = Y2, Y_1 = X2
    则X1 = X_1,Y1 = Y_1 - (a/b)*Y2
    则Y1 = Y_1 - (a/b)*X_1

    参考博客:
    https://blog.csdn.net/weixin_39645344/article/details/83615901

    见代码:

    //#define LL long long
    void exgcd(LL a,LL b,LL &x,LL &y,LL &d)//d is gcd(a,b) 
    {
    	if(b==0)
    	{
    		d = a, x = 1,y = 0;
    		return;
    	}
    	exgcd(b,a%b,y,x,d);
    	y -= (a/b)*x; //回溯
    }
    

    那么拓展欧几里得算法除了求这样的不定方程的解还能干什么呢?
    还能求乘法逆元!
    什么是乘法逆元呢?


    如果 a * b % m = 1,则我们称b为a关于模为m的乘法逆元


    (a + b) % mod = (a % mod) + (b % mod)
    (a - b) % mod = (a % mod) - (b % mod)
    (a * b) % mod = (a % mod) * (b % mod)
    (a / b) % mod = (a % mod) / (b % mod) <–这是错的!
    (a/b) %mod = 1 <=> a * inv(a) %mod = 1
    其中inv(a)是a关于mod的乘法逆元。满足a * inv(a) % mod = 1

    • 那如何求乘法逆元呢?我们可以用拓展欧几里得算法求解(还可以用 inv(a) = a^p % mod 求,其中p为euler(a)-1,其中euler(x)为欧拉函数,代表小于x的正整数中和x互质的数的个数)
    • 我们已经可以用exgcd求得 aX+bY = gcd(a,b)的解了
    • 还有一个很明显的结论:若x和mod不互质,则不存在x关于模为mod的乘法逆元
      因为假设这样的逆元为y,则有x * y % mod = 1, 令x = p*g, mod = q*g,(p,q互质),则 x * y % mod = p * g * y % (q * g), 结果为g的倍数,不是1

    所以,用exgcd可以求出aX+bY=gcd(a,b)=1的解
    设解为X=x0,Y=y0,
    则a * x0 = 1 - b * y0
    则a * x0 % b = 1
    故x0是a关于b的乘法逆元
    我们令x = x0,则x是答案,但x可能是负数,我们可以进行下面的操作,是x成为小于b的满足条件的正整数:
    x = ( (x % b) + b ) % b;
    见代码:

    //#define LL long long
    LL getInv(LL a,LL mod,LL &g)
    {
    	LL x,y,d;
    	exgcd(a,mod,x,y,d);
    	g = d;
    	return (x%mod+mod)%mod;
    }
    

    测试程序:

    #include <iostream>
    #include <iomanip>
    using namespace std;
    #define LL long long
    void exgcd(LL a,LL b,LL &x,LL &y,LL &d) 
    {
    	if(b==0)
    	{
    		d = a, x = 1,y = 0;
    		return;
    	}
    	exgcd(b,a%b,y,x,d);
    	y -= (a/b)*x;
    }
    LL getInv(LL a,LL mod,LL &g)
    {
    	LL x,y,d;
    	exgcd(a,mod,x,y,d);
    	g = d;
    	return (x%mod+mod)%mod;
    }
    int main()
    {
    	LL x,mod;
    	cout<<"求X关于mod的乘法逆元
    请输入X,mod: ";
    	while(cin>>x>>mod)
    	{
    		LL g;
    		LL inv = getInv(x,mod,g); 
    		if(g!=1)
    		{
    			printf("%lld和%lld不互质,没有逆元!
    
    请继续输入X,mod(ctrl z to exit): ",x,mod);
    			continue;
    		}
    		printf("%lld关于%lld的逆元为:%lld
    ",x,mod,inv);
    		printf("%lld * %lld %% %lld = %lld
    
    请继续输入X,mod(ctrl z to exit): ",x,inv,mod,x*inv%mod);		
    	}	
    	
    	
    	return 0;
    }
    
    
  • 相关阅读:
    颓废ing
    Linux下PF_PACKET的使用(todo)
    TIPC协议(todo)
    TOP命令详解(todo)
    so查找顺序
    把lib打包到so中
    -Bsymbolic: 优先使用本地符号
    GitHub 上最火的 Python 开源项目zz
    __attribute__((weak)) zz
    kmalloc(zz)
  • 原文地址:https://www.cnblogs.com/Apare-xzc/p/12243670.html
Copyright © 2011-2022 走看看