扩展欧几里得算法

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

求解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
posted @ 2020-10-08 16:55  Bekbek  阅读(265)  评论(0编辑  收藏  举报