头歌 经典密码体制(上)

文章详细介绍了移位密码(包括凯撒密码)、代换密码、仿射密码和维吉尼亚密码的基本原理和实现方法,涉及字符替换和模26的运算。通过实例展示了如何使用C++代码实现这些密码体制的加密和解密过程。
摘要由CSDN通过智能技术生成

个人关于头歌上相关案例的一些简单记录

移位密码

移位密码体制

通俗的来讲,移位密码体制就是通过一个线性变换,即一次方程,是使得在该变换下,明文和密文是一一对应的。下面给出具体的移位的密码体制。 令P=C=K=Z26​(这里的P表示明文空间,C代表密文空间,K代表密文空间)。对0<=K<=25,任意x,y属于Z26​,定义:

  • ek​(x)=(x+K)mod26,这个是加密式子,具体是对每一位明文字符x进行右移K位得到密文。

  • dk​(x)=(y−K)mod26,这个是解密式子,具体是对每一位密文字符y进行左移K位得到明文。

另外当K=3的时候,此密码体制叫做凯撒密码。

单看定义是不是很糊涂, 不要管, 记住这是凯撒密码就可以了。举例来说,如果密钥是3,则明文中的每个字母都向后移动3个位置,即A变成D,B变成E,以此类推。

代码

#include<bits/stdc++.h>
using namespace std;
char S[100];

//在下面Begin和End之间补全代码,对输入的字符串进行K位移动加密
int main()
{
    int K;
    scanf("%d",&K);
    cin>>S;
    
	/*********** Begin ***********/
	
    

    /*********** End ***********/
}

对于这个题干,K是秘钥,S是明文。
就是把S中的每个字母依次向后移动K位即可, 得解:

#include<bits/stdc++.h>
using namespace std;
char S[100];

//在下面Begin和End之间补全代码,对输入的字符串进行K位移动加密
int main()
{
    int K;
    scanf("%d",&K);
    cin>>S;
    
	/*********** Begin ***********/
	
    for (int i = 0; S[i] != '\0'; i++) {
        if (isalpha(S[i])) {
            char base = islower(S[i]) ? 'a' : 'A';
            S[i] = ((S[i] - base + K) % 26) + base;
        }
    }
    cout<<S<<endl;

    /*********** End ***********/
}

代换密码

代换密码体制

代换密码就是明文中每一个字符被替换成密文中的另外一个字符,代换后的各字母保持原来的位置。对密文进行逆替换就可以恢复出明文。

下面给出代换密码的具体定义。 令P=C=Z26​K26个数字0,1,...,25的所有可能代换组成。对任意的代换π属于K,定义: 加密函数: eπ​(x)=π(x) 解密函数: dπ​(y)=π−1(y) 这里的π−1代表置换π的逆代换。 在代换密码的情形下,可简单的将加密和解密过程直接看做是一个字母表上的置换。

同样,看定义乱七八糟的,核心就是替换。

测试说明

看测试说明就很好理解了


测试输入:

    mbgyalzvepsfonxqgduritwkch
    vgiyzmpxgbscutkrnhoafdqwel
    ggxrpnrvystmwcysyycqpevikeffmznimkkasvwsrenzkycx

预期输出:

    nnkabtaxysdvqeysyyerbgxfwgccvptfvwwzsxqsagtpwyek

输入的明文 x被替换为k,r替换为a,可以看到就是输入第一行的字母按照顺序被替换为第二行的字母

至于为什么初始的g被替换成了n而不是i
可以理解为发生了二次替换
在定义代换时, g一开始是被替换未i, 但在A字符串继续索引时, 会出现第二个g的替换
此时第二个g的替换会覆盖第一个g的替换, 导致最终的明文加密时是 g -> n 而不是 g -> i

理解了这一点代码就很容易了

C++支持直接用词典MAP的方式进行转换

代码

#include<bits/stdc++.h>
using namespace std;
char A[27],B[27],M[100];
map<char,char>MAP;

//在下面Begin和End之间补全代码,对输入的字符串进行代换表置换
int main()
{
    cin>>A;
    cin>>B;
    cin>>M;
 /*********** Begin ***********/
	for(int i=0;i<26;i++){
        MAP[A[i]]=B[i];
    }
    
    for(int i=0;M[i]!='\0';i++){
        if(MAP.find(M[i])!=MAP.end()){
            M[i]=MAP[M[i]];
        }
    }
    
    cout<<M<<endl;
 /*********** End ***********/
}

仿射密码

仿射密码为单表加密的一种,字母系统中所有字母都利用一个简单数学方程加密,对应至数值或转回字母。

仿射密码体制

P=C=Z26​K=(a,b)∈Z26​×Z26​:gcd(a,26)=1,对任意的K=(a,b)∈K,x,y∈Z26​,定义加密变换为: ek​(x)=(ax+b)(mod 26)

相应的解密变换为: dk​(y)=a−1(y−b)(mod 26) 下面给出一个例子: 设密钥K=(7,3),由前所得,有7−1(mod 26)=15。加密函数为ek​=7x+3(mod 26),因为7×15≡1(mod 26),相应的解密变换为: dk​(y)=15(y−3)(mod 26)=15y−45(mod 26) 上面的运算均是在Z26​上完成的。下面来验证对于任意的x∈Z26​,都有dk​(ek​(x))=x,下面是验证过程: dk​(ek​(x))=dk​(7x+3) =15(7x+3)−45 =x+45−45 =x 验证完毕。

直接看定义还是非常复杂, 关键是解密的变换甚至是写错的, 应该是下面这两个:
F​(x)=(ax+b)(mod 26) 加密
D(y) = a^-1 * (y - b) mod 解密

这里的 a^-1不是幂指数, 而是表示逆元
如果存在整数x使得 (a * x) mod m = 1,那么x就是a在模数m下的逆元

测试说明
测试输入:

    2 3
    qwercvzzxx

预期输出:

    asqqkcooii

测试输入:

    5 10
    qwercvzzxx

预期输出:

    gmuhslppnn

输入的两个字符就是 a和b , 对应输入字符串的每个字符就是密文y
因为只要解密, 所以看F​(y)=a^-1(y−b)](mod 26) 即可

代码

#include<bits/stdc++.h>
using namespace std;
char A[103];
constexpr int mod=26;

//在下面Begin和End之间补全代码,对输入的字符串进行仿射密码解密
/*********** Begin ***********/
size_t quick(size_t x,size_t y)
{
    size_t ans=x;
    while(y)
    {
        if(y&1)
            ans=ans*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return ans;
}
int main()
{
    int a,b;
    cin>>a>>b;
    cin>>A;
    int x=quick(a,15);
    for(int i=0; i<strlen(A); i++)
        printf("%c",'a'+(x*((A[i]-'a'-b+mod)%mod)%mod));
    return  0;
}
/*********** End ***********/

维吉尼亚密码

维吉尼亚密码的密码体制

维吉尼亚密码(又译维热纳尔密码)是使用一系列凯撒密码组成密码字母表的加密算法,属于多表密码的一种简单形式。下面给出具体定义。 设m是一个正整数,定义P=C=K=(Z26​)m(这里之所以会多出一个m次方,就是因为维吉尼亚密码是一个多表代换,即对于相同的明文可以对应不同的密文)。对任意的密钥K=(k1​,k2​,...,km​),定义: ek​(x1​,x2​,...,xm​)=(x1​+k1​,x2​+k2​,...,xm​+km​) 这个是加密函数。 dk​(y1​,y2​,...,ym​)=(y1​−k1​,y2​−k2​,...,ym​−km​) 这个是解密函数。 以上所有的运算都是在Z26​上进行。

看之后的例子

假设m=6,密钥字为“CIPHER”,其对应于如下的数字串K=(2,8,15,7,4,17)。要加密的明文为thiscryptos。 那么我们可以把明文转化为对应的数字,使用密钥字进行模26的运算。
那么相应的密文为:vpxzgiaxivw

代码

#include<bits/stdc++.h>
using namespace std;
char A[103],B[103];
//在下面Begin和End之间补全代码,对输入的字符串进行维吉尼亚加密
int main()
{
    cin>>A;
    cin>>B;
    /*********** Begin ***********/
     // 将密钥串和明文串都转换为小写字母
    for(int i=0; A[i]!='\0'; i++){
        A[i] = tolower(A[i]);
    }
    for(int i=0; B[i]!='\0'; i++){
        B[i] = tolower(B[i]);
    }
    
    int keyLen = strlen(A);
    int textLen = strlen(B);
    
    string encryptedText = "";
    
    for(int i=0; i<textLen; i++){
        char keyChar = A[i % keyLen]; // 循环使用密钥中的字符
        char plainChar = B[i];
        
        // 对明文字符进行移位加密
        char encryptedChar = 'a' + (plainChar - 'a' + keyChar - 'a') % 26;
        
        // 将加密后的字符加入到加密串中
        encryptedText += encryptedChar;
    }
    
    // 输出密文串
    cout << encryptedText;

    /*********** End ***********/
}

  • 20
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值