第三章 编程习题之 Hill密码的实现

HIll密码的实现

第一次尝试不完美:

1. 信息:

  • 输入:一个明文字符串和一个加密矩阵(通常是一个2x2或3x3矩阵)。
  • 输出:加密后的密文字符串。
  • 假设:所有字符都是大写或小写的英文字母。

2. 分析:

  • 加密过程:将明文分割成与矩阵大小相等的块,然后将每个块乘以加密矩阵,得到加密后的密文块。
  • 解密过程:需要计算加密矩阵的逆矩阵,并用它来解密密文。

3. 算法设计:

  1. 选择加密矩阵:确定一个可逆的2x2或3x3矩阵作为加密矩阵。
  2. 文本处理:将明文转换为数字(A=0, B=1, ..., Z=25)。
  3. 加密
    • 分组:将数字分成大小与矩阵相匹配的组。
    • 矩阵乘法:将每个组与加密矩阵相乘,然后取模26。
  4. 解密
    • 计算加密矩阵的逆矩阵。
    • 使用逆矩阵对密文进行同样的矩阵乘法操作。

4. 代码实现(用C++):

#include<iostream>
#include<vector>

// 函数来计算模26下的逆
int modInverse(int a, int m) {
    for (int x = 1; x < m; x++)
        if (((a%m) * (x%m)) % m == 1)
            return x;
    return 1;
}

// Hill加密函数
std::string hillEncrypt(std::string plaintext, std::vector<std::vector<int>> keyMatrix) {
    std::string ciphertext = "";

    // 确保文本长度是偶数
    if (plaintext.length() % 2 != 0) {
        plaintext += "X"; // 填充字符
    }

    for (int i = 0; i < plaintext.length(); i += 2) {
        // 对每个2x1的明文向量进行加密
        int val1 = (keyMatrix[0][0] * (plaintext[i] - 'A') + keyMatrix[0][1] * (plaintext[i + 1] - 'A')) % 26;
        int val2 = (keyMatrix[1][0] * (plaintext[i] - 'A') + keyMatrix[1][1] * (plaintext[i + 1] - 'A')) % 26;

        // 将加密后的数字转换回字符
        ciphertext += (val1 + 'A');
        ciphertext += (val2 + 'A');
    }
    return ciphertext;
}

int main() {
    std::string plaintext = "HELLO";
    std::vector<std::vector<int>> keyMatrix = {{5, 17}, {8, 3}}; // 一个示例密钥矩阵

    std::string encryptedText = hillEncrypt(plaintext, keyMatrix);
    std::cout << "Encrypted Text: " << encryptedText << std::endl;

    // 解密过程将类似,但使用加密矩阵的逆矩阵
    return 0;
}

5. 实现代码过程中可能遇到的问题:

  • 逆矩阵不存在:如果加密矩阵不可逆(即其行列式等于0或与26不互质),则无法进行加密和解密。
  • 字符处理:所有输入字符必须是大写或小写,并且长度应与矩阵大小兼容。
  • 性能问题:对于更长的文本和更大的矩阵,计算可能会变得复杂和耗时。

Hill密码是对称密钥加密的一个基本示例,更复杂的实现可能包括错误检查、动态矩阵大小以及对各种字符的支持。

六、复盘

不足:这个代码是提前给定数制的不是用户输入,有缺陷。

第二次尝试:

#include<iostream>
#include<vector>
#include<string>

// 函数来计算模26下的逆
int modInverse(int a, int m) {
    a = a % m;
    for (int x = -m; x < m; x++)
        if ((a * x) % m == 1)
            return x;
    return 1;
}

// Hill加密函数
std::string hillEncrypt(const std::string &plaintext, const std::vector<std::vector<int>> &keyMatrix) {
    std::string ciphertext = "";

    // 确保文本长度是矩阵大小的倍数
    int size = keyMatrix.size();
    while (plaintext.length() % size != 0) {
        plaintext += "X"; // 填充字符
    }

    for (int i = 0; i < plaintext.length(); i += size) {
        for (int row = 0; row < size; ++row) {
            int value = 0;
            for (int col = 0; col < size; ++col) {
                value += keyMatrix[row][col] * (plaintext[i + col] - 'A');
            }
            ciphertext += (char)((value % 26) + 'A');
        }
    }
    return ciphertext;
}

int main() {
    std::string plaintext;
    std::cout << "Enter the plaintext: ";
    std::cin >> plaintext;

    int matrixSize = 2; // 或者从用户那里获取矩阵大小
    std::vector<std::vector<int>> keyMatrix(matrixSize, std::vector<int>(matrixSize));

    std::cout << "Enter the " << matrixSize * matrixSize << " matrix elements:\n";
    for (int i = 0; i < matrixSize; ++i) {
        for (int j = 0; j < matrixSize; ++j) {
            std::cin >> keyMatrix[i][j];
        }
    }

    std::string encryptedText = hillEncrypt(plaintext, keyMatrix);
    std::cout << "Encrypted Text: " << encryptedText << std::endl;

    // 对于解密,你会需要计算密钥矩阵的逆并执行相似的过程。
    return 0;
}

说明:

在这个改进的版本中:

  • 用户可以输入自己的明文。
  • 用户还可以输入自己的密钥矩阵,它必须是可逆的。

这种设计更加灵活和实用,因为它允许用户根据自己的需要来选择密钥,使得加密过程更加安全。同时,这也使得程序能够适应不同大小的密钥矩阵,提高了其适用性。不过,这个简单的示例没有实现解密过程,也没有错误处理机制,例如检查矩阵是否可逆,或者输入是否有效,这些在实际应用中是必须的。

运行结果:

有读者知道问什么报错了?

答案:在C++中,我不能直接在函数参数中修改传递的字符串,因为它是以const引用传递的。你需要在函数内部创建一个局部副本来修改。

第三次尝试:

#include<iostream>
#include<vector>
#include<string>

// 函数来计算模26下的逆
int modInverse(int a, int m) {
    a = a % m;
    for (int x = -m; x < m; x++) {
        if ((a * x) % m == 1) {
            return x;
        }
    }
    return 1; // 如果没有找到逆元,这可能表示某些错误
}

// Hill加密函数
std::string hillEncrypt(const std::string &input, const std::vector<std::vector<int>> &keyMatrix) {
    std::string plaintext = input;  // 创建一个可修改的副本
    std::string ciphertext = "";

    // 确保文本长度是矩阵大小的倍数
    int size = keyMatrix.size();
    while (plaintext.length() % size != 0) {
        plaintext += "X"; // 填充字符
    }

    for (int i = 0; i < plaintext.length(); i += size) {
        for (int row = 0; row < size; ++row) {
            int value = 0;
            for (int col = 0; col < size; ++col) {
                value += keyMatrix[row][col] * (plaintext[i + col] - 'A');
            }
            ciphertext += (char)((value % 26) + 'A');
        }
    }
    return ciphertext;
}

int main() {
    std::string plaintext;
    std::cout << "Enter the plaintext: ";
    std::cin >> plaintext;

    int matrixSize;
    std::cout << "Enter the size of the matrix (e.g., 2 for 2x2): ";
    std::cin >> matrixSize;
    
    std::vector<std::vector<int>> keyMatrix(matrixSize, std::vector<int>(matrixSize));

    std::cout << "Enter the " << matrixSize * matrixSize << " matrix elements (row by row):\n";
    for (int i = 0; i < matrixSize; ++i) {
        for (int j = 0; j < matrixSize; ++j) {
            std::cin >> keyMatrix[i][j];
        }
    }

    std::string encryptedText = hillEncrypt(plaintext, keyMatrix);
    std::cout << "Encrypted Text: " << encryptedText << std::endl;

    return 0;
}

说明:

此代码读取用户的明文和密钥矩阵,然后使用Hill加密算法进行加密。注意,这个实现假设所有输入都是大写字母,并且用户输入的密钥矩阵是可逆的。在实际应用中,你可能还需要添加错误检查以确保密钥矩阵的有效性,以及其他安全性和健壮性措施。

运行结果:

还没输入完就爆炸哈哈哈🤣

有读者知道原因吗?

答案:这个问题是因为在请求矩阵大小之后立即尝试读取矩阵元素,但没有正确地获取矩阵的大小。这可能是由于在读取矩阵大小后,输入缓冲区中留下了换行符,导致下一个输入(矩阵大小)被跳过。这是C++中常见的问题,可以通过在读取大小之后清除输入缓冲区来解决。 

第四次尝试最终版:

#include<iostream>
#include<vector>
#include<string>
#include<limits>  // for std::numeric_limits

// 函数来计算模26下的逆
int modInverse(int a, int m) {
    a = a % m;
    for (int x = -m; x < m; x++) {
        if ((a * x) % m == 1) {
            return x;
        }
    }
    return 1; // 如果没有找到逆元,这可能表示某些错误
}

// Hill加密函数
std::string hillEncrypt(const std::string &input, const std::vector<std::vector<int>> &keyMatrix) {
    std::string plaintext = input;  // 创建一个可修改的副本
    std::string ciphertext = "";

    // 确保文本长度是矩阵大小的倍数
    int size = keyMatrix.size();
    while (plaintext.length() % size != 0) {
        plaintext += "X"; // 填充字符
    }

    for (int i = 0; i < plaintext.length(); i += size) {
        for (int row = 0; row < size; ++row) {
            int value = 0;
            for (int col = 0; col < size; ++col) {
                value += keyMatrix[row][col] * (plaintext[i + col] - 'A');
            }
            ciphertext += (char)((value % 26) + 'A');
        }
    }
    return ciphertext;
}

int main() {
    std::string plaintext;
    std::cout << "Enter the plaintext: ";
    std::getline(std::cin, plaintext);  // 使用getline来读取一行文本

    int matrixSize;
    std::cout << "Enter the size of the matrix (e.g., 2 for 2x2): ";
    std::cin >> matrixSize;

    // 清除输入缓冲区
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    std::vector<std::vector<int>> keyMatrix(matrixSize, std::vector<int>(matrixSize));

    if (matrixSize > 0) {
        std::cout << "Enter the " << matrixSize * matrixSize << " matrix elements (row by row):\n";
        for (int i = 0; i < matrixSize; ++i) {
            for (int j = 0; j < matrixSize; ++j) {
                std::cin >> keyMatrix[i][j];
            }
        }

        std::string encryptedText = hillEncrypt(plaintext, keyMatrix);
        std::cout << "Encrypted Text: " << encryptedText << std::endl;
    } else {
        std::cout << "Matrix size must be greater than 0." << std::endl;
    }

    return 0;
}

说明:

在这个修正版中,我使用了std::getline来读取完整的文本行,这样就不会留下未处理的换行符。然后,使用std::cin.ignore在读取矩阵大小后清除输入缓冲区,这样就不会有未处理的字符影响后续的输入。此外,我还添加了一个检查来确保矩阵大小大于0。这些改动应该会解决程序提前退出的问题。

运行结果:

1. 信息(题目的有用信息):

  • 输入: 用户需要输入一段明文和一个加密矩阵的大小及其元素。
  • 处理: 程序应该能够接收这些输入,处理它们,并进行Hill加密。
  • 输出: 加密后的文本。

2. 分析(包括每个信息的作用和思考过程和分析过程):

  • 明文处理: 明文可能包含空格和不同大小写的字符,需要统一格式并处理额外字符。
  • 矩阵大小输入: 用户输入的矩阵大小决定了如何读取和构建矩阵,以及如何将明文分组进行加密。
  • 矩阵元素输入: 矩阵元素是加密的核心,必须正确读取和存储。
  • 加密处理: 根据Hill加密算法,明文会被分成块,每个块与矩阵相乘后进行模26运算得到密文。
  • 输入验证和错误处理: 程序需要能够处理非预期输入,如非法矩阵大小和字符。

3. 算法设计:

  1. 读取输入: 使用std::getline读取明文,用std::cin读取矩阵大小和元素。
  2. 格式化和验证: 确保明文只包含有效字符,并验证矩阵大小正确。
  3. 构建矩阵: 根据用户输入的大小和元素构建加密矩阵。
  4. 加密过程:
    • 将明文分组,每组大小等于矩阵的行数。
    • 对每组进行矩阵乘法和模26运算。
    • 将结果转换回字符形式并拼接成密文。
  5. 输出结果: 显示加密后的密文。

4. 代码实现(用C++):

请参考之前的回答中提供的完整C++代码。代码实现了上述算法设计的所有步骤。

5. 实现代码过程中可能遇到的问题:

  • 输入处理问题: 处理用户输入时,特别是字符串和数值混合输入,可能会遇到问题。例如,未处理的换行符或非预期的字符类型可能导致程序行为异常。
  • 矩阵验证: 如果用户输入的矩阵不是可逆的,那么加密过程就会失败。实际实现中需要检查矩阵是否可逆。
  • 字符范围限制: 程序目前只处理大写字母。如果输入包含小写字母、数字或特殊字符,可能会导致不正确的加密结果。
  • 错误反馈: 如果用户输入无效数据(如负矩阵大小),程序应提供清晰的错误消息而不是异常退出。
  • 性能问题: 对于非常大的输入,当前的实现可能会遇到性能瓶颈。优化算法或改进数据结构可能是必要的。

总结:

学到了什么?

1. 错误处理与输入验证:

  • 观察: 代码中原先未能处理意外输入,如非数字字符或不合适的矩阵大小。
  • 学习: 强化了输入验证和错误处理的重要性。无论输入看起来多么简单,都应该验证并准备好处理非预期的或无效的输入。
  • 改进方法: 学习和实现各种输入验证技术,比如在接收用户输入之后进行条件检查,使用异常处理机制等。

2. 编程语言特性理解:

  • 观察: 最初的代码试图修改一个以const引用传递的字符串。
  • 学习: 加深了对C++语言特性,特别是关于const正确性和变量作用域的理解。
  • 改进方法: 通过阅读文档、书籍或教程,增强对编程语言特性的理解。实践通过编写和修改代码来巩固知识。

3. 算法理解与实现:

  • 观察: 实现Hill密码涉及到矩阵运算和模运算,需要准确理解算法。
  • 学习: 体会到了深入理解算法的重要性,以及如何将算法准确转化为代码的能力。
  • 改进方法: 通过不断练习,尝试实现和优化不同的算法,提高将复杂问题简化为代码的能力。

4. 调试与测试:

  • 观察: 初始代码中的错误在实际运行时才显现出来。
  • 学习: 了解了编写代码时进行彻底测试的重要性,包括考虑边界条件和异常情况。
  • 改进方法: 学习使用调试工具和技术,如断点、步进执行和查看变量值。编写单元测试来自动检查代码的正确性。

5. 清晰的代码结构:

  • 观察: 代码需要清晰和易于理解,不仅对自己,也对未来的维护者。
  • 学习: 清晰的代码结构和良好的命名规范可以显著提高代码的可读性和可维护性。
  • 改进方法: 练习编写组织良好、结构清晰的代码。学习和遵循最佳实践,比如代码重构、使用设计模式等。

6. 持续学习与反思:

  • 观察: 解决问题过程中遇到的挑战要求不断学习和适应。
  • 学习: 程序设计和编码是一个持续学习和改进的过程。
  • 改进方法: 保持好奇心,对遇到的每一个错误和挑战进行深入研究。定期回顾和反思自己的代码,学习新技术和方法。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

夏驰和徐策

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

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

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

打赏作者

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

抵扣说明:

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

余额充值