倪文迪陪你学蓝桥杯2021寒假每日一题:1.27日(2019省赛A组第5题)

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

1、题目描述

  仍然是填空。


RSA 是一种经典的加密算法。它的基本加密过程如下。
首先生成两个质数 p , q p, q p,q,令 n = p ⋅ q n = p · q n=pq,设 d d d ( p − 1 ) ⋅ ( q − 1 ) (p-1) · (q-1) (p1)(q1) 互质,则可找到 e e e 使得 d ⋅ e d · e de ( p − 1 ) ⋅ ( q − 1 ) (p-1) · (q- 1) (p1)(q1) 的余数为 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=3n=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,请问,原文是多少?


2、题解

2.1 求p、q

  先求 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=891234941q=1123984201

1、C++代码
  执行实际约十秒。

//大概10秒
#include<bits/stdc++.h>
#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)

2.2 求e

  这时候要用到真正的大数了。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,求第一个就行了

2.3 求 X = C e m o d   n X = C^e mod \ n X=Cemod n

   原来,本题是考了一个快速幂。还是用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

3、快速幂

  快速幂的原理,在《算法竞赛入门到进阶》京东 当当156页做了清晰简明的解释。大家可能没有这本书,这里贴图:

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗勇军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值