2021年寒假每日一题,2017~2019年的省赛真题。本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供。每日一题,关注蓝桥杯专栏: https://blog.csdn.net/weixin_43914593/category_10721247.html
每题提供C++、Java、Python三种语言的代码。
2019省赛A组第5题“RSA解密” ,题目链接:
http://oj.ecustacm.cn/problem.php?id=1456
仍然是填空。
RSA 是一种经典的加密算法。它的基本加密过程如下。
首先生成两个质数 p , q p, q p,q,令 n = p ⋅ q n = p · q n=p⋅q,设 d d d与 ( p − 1 ) ⋅ ( q − 1 ) (p-1) · (q-1) (p−1)⋅(q−1) 互质,则可找到 e e e 使得 d ⋅ e d · e d⋅e 除 ( p − 1 ) ⋅ ( q − 1 ) (p-1) · (q- 1) (p−1)⋅(q−1) 的余数为 1。
n , d , e n, d, e n,d,e 组成了私钥, n , d n, d n,d 组成了公钥。
当使用公钥加密一个整数 X X X 时(小于 n n n),计算 C = X d m o d n C = X^d mod\ n C=Xdmod n,则 C C C 是加密后的密文。
当收到密文 C C C 时,可使用私钥解开,计算公式为 X = C e m o d n X = C^e mod \ n X=Cemod n。
例如,当 p = 5 , q = 11 , d = 3 时 , n = 55 , e = 27 p = 5, q = 11, d = 3 时,n = 55, e = 27 p=5,q=11,d=3时,n=55,e=27。
若加密数字 24,得 2 4 3 m o d 55 = 19 24^3 mod\ 55 = 19 243mod 55=19。
解密数字 19,得 1 9 2 7 m o d 55 = 24 19^27 mod\ 55 = 24 1927mod 55=24。
现在你知道公钥中 n = 1001733993063167141 , d = 212353 n = 1001733993063167141, d = 212353 n=1001733993063167141,d=212353,同时你截获了别人发送的密文 C = 20190324 C = 20190324 C=20190324,请问,原文是多少?
先求 n n n的素因子 p p p和 q q q。注意,n只有这2个因子,没有别的因子。 p p p和 q q q必然有 一个小于 n \sqrt n n,找到一个,另一个就知道了。
没有什么好办法,只能暴力,也就是简单地用 i i i循环从2到 n \sqrt n n一个个试。若 n n n除以 i i i,余数是0, i i i就是因子。
如果预先知道素数序列,只试这些素数,当然能更快。不过,用素数筛预计算出比 n \sqrt n n小的素数,也需要至少 n \sqrt n n次。还不如直接用暴力。
下面的代码,循环次数是 n \sqrt n n= 1001733993063167141 = 1000866621 \sqrt {1001733993063167141}=1000866621 1001733993063167141=1000866621,即十亿次计算。得到: p = 891234941 、 q = 1123984201 p=891234941、q=1123984201 p=891234941、q=1123984201。
1、C++代码
执行实际约十秒。
//大概10秒
#include
#define ll long long
using namespace std;
int main(){
ll n = 1001733993063167141;
ll k= sqrt(n);
for(ll i = 2 ; i < k; i++)
if(n % i == 0)
cout << i << " " << n / i << endl;
return 0;
}
2、python代码
竟然要几分钟!Python在做循环的时候失去了威力。
网上有大量吐槽Python的**for
循环慢**的帖子。
from math import *
n = 1001733993063167141
k = int(sqrt(n))
for i in range(2,k):
if n%i == 0:
print(i,n//i)
这时候要用到真正的大数了。c++的64位long long不够用,虽然有_int128,但是有些编译器不支持。
还是靠Python吧。下面代码打印出e=823816093931522017。注意e有很多个,取最小的一个就行了。
n = 1001733993063167141
d = 212353
p=891234941
q=1123984201
tmp = (p - 1) * (q - 1)
print(tmp)
for i in range(2,n+1):
now = i * tmp + 1
if (now % d == 0):
print(now // d) #打印e
break #有很多e,求第一个就行了
原来,本题是考了一个快速幂。还是用Python吧:
def qpow(a,b,mod):
ret = 1
while b:
if(b&1):
ret = ret*a % mod
a = a*a % mod
b>>=1
return ret
n = 1001733993063167141
e = 823816093931522017 #试试其他的e
C = 20190324
print(qpow(C,e,n)) #579706994112328949
快速幂的原理,在《算法竞赛入门到进阶》京东 当当156页做了清晰简明的解释。大家可能没有这本书,这里贴图: