一 欧几里得算法(辗转相乘法)
求解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