第一题:幂数加密
查看附件:
第一题分析:
他说答案是八位的大写字母,大写字母要么是ASCII表上的65-90,要么就0-25(或1-26)也是可以表示。但是既然说明是八位,那这个八位肯定要体现出来,也就是说这个附件要能够搞出八个数码来代表大写字母。我们先得到这个字符串的长度:34,这不能被8整除,那是否有别的字符能标识8段呢?
于是我写了这样一个程序:
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
map<char, int> mp;
string str = "8842101220480224404014224202480122";
for (int i = 0; i < (int)str.length(); ++i)
mp[str[i]]++;
for (map<char, int>::iterator it = mp.begin(); mp.end() != it; ++it)
cout << it->first << ' ' << it->second << endl;
return 0;
}
结果如下:
首先我们知道,要把一段序列分成8段,就必须某个字符要出现7次,这里出现7次的唯独’0’这个字符,那我们就不妨以0为分界线来划分出8个数字码:
合起来就是:
WELLDONE(翻译就是干得漂亮?做得很好?)觉得还像那么回事,于是提交!果然AC!
第二题:easy_RSA
下载附件:
说实话,我喜欢做这种题,因为我本身是做公钥密码学大类的(现在密码学研究都是公钥密码甚至是同态密码,前几道对称密码我不太了解……)
讲讲RSA算法:
首先拿到素数p和q,然后计算n = p * q,phi(n) = (p - 1) * (q - 1)(欧拉函数,不晓得请自行了解),然后已知公钥e = 17,可以计算私钥d = e^(-1) mod( phi(n) )
也就是说,公钥和私钥互为逆元,这里要我们求私钥d,相当于求逆元!如何求逆元?
求逆元的简单方法:
可以用快速幂求逆元,当然也可以用拓展欧几里得算法求逆元,我们就讲一种比较简单的方法求逆元。已知逆元的定义:
a * b mod p = 1
我们就说:a和b互为模p意义下的逆元
由费马小定理得到:a^(p - 1) mod p = 1,那也就是说a * a^(p - 2) modp = 1,也就是说求a在模p意义下的逆元,就是求 a ^(p - 2)
但是这个方法的使用是有条件的!那就是p是素数,且a < p,而咱们这个题,p是素数吗?模数不是素数,其实在RSA算法中,模数都不会是素数,因为p和q是不相同的素数,素数唯一的偶数是2,而p和q之中至少有一个不是2,则是奇数,那么p(或者q) - 1 就一定是偶数,偶数乘以任何数都是偶数,则必然模数不是质数!!
求逆元的通用方法:拓展欧几里得算法
为什么可以用扩展欧几里得算法呢?
先来看一个方程:
ax + by = 1
如果两边同时对b取模,则:ax mod b = 1
那么a和x就是模b意义下的逆元,而我们告知a和b,求出x相当于求方程的解,由此可以得出用拓展欧几里得算法求逆元!
代码如下:
#include <iostream>
#define LL long long
using namespace std;
void extendGcd(LL a, LL b, LL& x, LL& y)
{
if (0 == b)
{
x = 1, y = 0;
return;
}
extendGcd(b, a % b, x, y);
LL Temp = x;
x = y;
y = Temp - (a / b) * y;
}
int main()
{
LL p = 473398607161, q = 4511491, e = 17;
LL phi = (p - 1) * (q - 1);
LL x, y;
extendGcd(e, phi, x, y);
cout << x;
return 0;
}
结果展示:
写在后面:
扩展欧几里得算法和费马小定理求逆元,是两种最常见的求逆元方法,此外还有筛法、递推等等办法,不过基本上,用拓展欧几里得就差不多可以搞定全部了!求逆元是数论、密码学中常见的计算需求!