zoukankan      html  css  js  c++  java
  • 扩展欧几里得算法

    一 欧几里得算法(辗转相乘法)

    求解a,b的最大公约数:gcd(a,b) = gcd(b,a%b)

    二 扩展欧几里得算法

    扩展欧几里得算法是对欧几里得算法的扩展,它不仅能找到最大公约数,还能找到其整数解x,y(其中可能有负数)。

    我们知道对于两个数a,b,存在x,y使得ax + by = gcd(a,b)。当b = 0时,ax+by = a,所以x = 1,y = 0。当b!=0时,根据欧几里得算法有:

    ax1 + by1 = gcd(a,b) = gcd(b,a%b) = bx2 + (a%b)y2 = bx2 + (a - a/b*b)y2 = ay2 + (x2 - (a/b)y2)b

    因为a , b为定值 所以

    x1 = y2

    y1 = x2 - (a/b)y2

    这样我们在计算最大公约数的过程中就能倒着求解出x,y。

    gcd(x,y)=ax+by(贝祖定理)
    
    
    #include<iostream>
    
    #include<cstdio>
    
    #include<cmath>
    
     
    
    using namespace std;
    
     
    
    int exgcd(int a,int b,int &x,int &y)//扩展欧几里得算法
    
    {
    
        if(b==0)
    
        {
    
            x=1;y=0;
    
            return a;  //到达递归边界开始向上一层返回
    
        }
    
        int r=exgcd(b,a%b,x,y);
    
        int temp=y;    //把x y变成上一层的
    
        y=x-(a/b)*y;
    
        x=temp;
    
        return r;     //得到a b的最大公因数
    
    }
    

    三 利用扩展欧几里得求解逆元

    逆元:模n意义下,1个数a如果有逆元x,那么除以a相当于乘以x。

    Zn* = {0 < x < n && gcd(x,n) = 1}

    乘法逆元:对于集合Zn *中的元素,每个数a均有唯一的与之对应的乘法逆元x,使得ax≡1(mod n)。

    ax≡1(mod n) 即 gcd(a,n) = 1. 故有 ax + ny = 1 ,我们利用扩展欧几里得算法便可求解出gcd(a,n)和 x,如果gcd(a,n) = 1 说明逆元x存在,我们只要将其范围变为0~n-1之间即可,若gcd不为1则说明逆元不存在。

    int mod_reverse(int a, int n)//ax=1(mod n) 求a的逆元x 
    {
    	int x, y;
    	int r = exgcd(a, n, x, y);
    	if (r == 1) {
    		x = (x%n + n) % n;
    		return x;
    	}
    	return -1;
    }
    
    

    当几个数连续乘最后取模时,可以将每个数字先取模,最后再取模,即%对于*具有结合律。但是如果当用来取模的数本身就很大,采取上述方法就不行了。这个时候可以借鉴快速幂取模的方法,来达到大数相乘取模的效果。

    ll qmul(ll x, ll y, ll mod)   // 乘法防止溢出, 如果p * p不爆LL的话可以直接乘; O(1)乘法或者转化成二进制加法(快速加)
    {
        ll ret = 0;
        while(y) {
            if(y & 1)
                ret = (ret + x) % mod;
            x = x * 2 % mod;
            y >>= 1;
        }
        return ret;
    }
    ll qpow(ll a, ll n, ll mod)
    {
        ll ret = 1;
        while(n)
        {
            if(n & 1) ret = qmul(ret, a, mod);
            a = qmul(a, a, mod);
            n >>= 1;
        }
        return ret;
    }
    

    四 2019蓝桥CA

    【问题描述】
    RSA是一种经典的加密算法。它的基本加密过程如下。

    首先生成两个大质数p,q, 令n = pq,设d与(p-1)(q-1)互质,则可以找到e,使得de除以(p-1)(q-1)的余数为1

    n,d,e组成了私钥,n,d构成了公钥。

    当使用公钥加密一个整数X时(X<=n-1),计算C = X^d mod n,则C是加密后的密文。

    当收到密文C时,可以使用私钥解开,计算公式为:X = C^e mod n。

    例如:当p = 5, q = 11, n = 55, e = 27。

    若加密数字24,得24^3 % 55 = 19。

    解密数字19,得19^27 % 55 = 24。

    现在你知道公钥中n = 1001733993063167141,d = 212353,同时,你截获了别人发送的密文C = 20190324,请问,原文是多少?

    #include<iostream>
    #include<algorithm>
    #include<vector>
    using namespace std;
    
    #define ll long long
    
    ll exgcd(ll a, ll b, ll &x, ll &y) {
    	
    	if (b == 0) {
    		x = 1;
    		y = 0;
    		return a;
    	}
    
    	ll r = exgcd(b, a%b, x, y);
    	ll tmp = x;
    	x = y;
    	y = tmp - (a / b)*y;
    	return r;
    
    }
    
    
    ll mod_reverse(ll a, ll n)//ax=1(mod n) 求a的逆元x 
    {
    	ll x, y;
    	ll r = exgcd(a, n, x, y);
    	if (r == 1) {
    		x = (x%n + n) % n;
    		return x;
    	}
    	return -1;
    }
    
    ll qmul(ll x, ll y, ll mod)   // 乘法防止溢出, 如果p * p不爆LL的话可以直接乘; O(1)乘法或者转化成二进制加法(快速加)
    {
    	ll ret = 0;
    	while (y) {
    		if (y & 1)
    			ret = (ret + x) % mod;
    		x = x * 2 % mod;
    		y >>= 1;
    	}
    	return ret;
    }
    ll qpow(ll a, ll n, ll mod)
    {
    	ll ret = 1;
    	while (n)
    	{
    		if (n & 1) ret = qmul(ret, a, mod);
    		a = qmul(a, a, mod);
    		n >>= 1;
    	}
    	return ret;
    }
    
    
    int main()
    {
    	ll p, q;
    	ll n = 1001733993063167141;
    
    	//暴力求pq
    	//for (int i = 1e8 + 1; i < n; i++) {
    	//	if (n%i == 0) {
    	//		cout << i << endl << n / i;
    	//		break;
    	//	}
    	//}
    	p = 891234941;
    	q = 1123984201;
    	ll e = mod_reverse(212353, (p-1)*(q-1));
    	cout << e << endl;
    	ll ans = qpow(20190324, e, n);
    	cout <<ans<< endl;
    	system("pause");
    	return 0;
    }
    
    //ans = 579706994112328949
    
  • 相关阅读:
    华为上机练习题--求两个数组的总和
    C++设计模式之状态模式(四)
    深入理解java嵌套类和内部类
    c++实现精确计时
    Linux-中断和中断处理
    使用C#对MongoDB中的数据进行查询,改动等操作
    淘特房产CMS系统 7.5
    sass03 变量、样式导入
    sass02
    sass01
  • 原文地址:https://www.cnblogs.com/muyefeiwu/p/13781745.html
Copyright © 2011-2022 走看看