【问题标题】:How do I get two different random numbers in the same srand() seed? C++如何在同一个 srand() 种子中获得两个不同的随机数? C++
【发布时间】:2021-07-08 04:41:36
【问题描述】:

我正在尝试创建一个随机密码生成器。当我多次调用该函数时,它返回相同的字符值。 QQpQ;7Q7pQ;p

我尝试在我的主函数中添加srand(time(0));srand((unsigned int) time(NULL));

查看其他 stack over flow 帖子,我发现 srand 有助于在我每次运行程序时创建不同的字符。但是它仍然返回相同字符的倍数。

如何在运行时更改 srand() 种子以获得不同的随机数?

#include <string>
#include <iostream>
#include <time.h>

using namespace std;

string randomGenerator(int numberOfCharacters);
char randomCapitalCharacter();
char randomLowerCharacter();
char randomSpecialCharacter();
char randomInt();

int main(){
    srand((unsigned int) time(NULL));
    int passwordLength = 12;
    string password = randomGenerator(passwordLength);
    cout << password << endl;
    return 0;
}

string randomGenerator(int numberOfCharacters){
    char randomCharacterTypes[4] = {randomLowerCharacter(),randomInt(), randomCapitalCharacter(), randomSpecialCharacter()};
    std::string password;

    while (numberOfCharacters > 0){
        int random = rand()%4;
        password += randomCharacterTypes[random];
        numberOfCharacters--;
    }
    return password;
}
char randomInt(){
    std::string numberAsString = to_string(std::rand()% 10);
    char randomNumberChar = numberAsString.at(0);
    return randomNumberChar;
}
char randomLowerCharacter(){
    return 97 + rand()%26; //97 = a
}
char randomCapitalCharacter(){
    return 65 + rand()%26; //65 = A
}
char randomSpecialCharacter(){
    /** Special characters are split by numbers so we pick a random from one of the 2 groups.*/
    return (rand()%2) ? char(33 + rand()%14) : char(58 + rand()%8); 
}

【问题讨论】:

  • randomCharacterTypes[4] 只包含 4 个不同的字符,您的意思是创建一个函数指针数组吗?
  • 当我运行你的代码时,第一次打印RReR&gt;&gt;eReR6e,第二次打印g&gt;g&gt;7gTgg&gt;&gt;T是不是因为你在同一秒内运行了不止一次?这将给出相同的值,因为 time(NULL) 返回一个整数秒 - 所以如果你在同一秒内多次运行它 time(NULL) 将返回相同的值。
  • 由于您使用的是 c++11,因此您应该停止使用 srand 以支持新的随机数生成器:stackoverflow.com/a/66850447/1294207
  • @AlanBirtles 哦,我明白了,我正在尝试使用函数指针。我看到如果我只是调用 randomLowerCharacter();它确实返回一个随机字符值。我将研究如何实现函数指针。谢谢!!!
  • 如果你在代码中写'a'而不是97,你可以避免像//97 = a这样的cmets(其他字符也类似)..

标签: c++ c++11 random srand


【解决方案1】:

randomCharacterTypes 是一个由 4 个字符组成的数组,对于每个字符类,您总是会得到相同的字符。如果你想每次都生成一个新字符,你可以使用函数指针来代替:

string randomGenerator(int numberOfCharacters){
    char (*randomCharacterTypes[])() = {randomLowerCharacter,randomInt, randomCapitalCharacter, randomSpecialCharacter};
    std::string password;

    while (numberOfCharacters > 0){
        int random = rand()%4;
        password += randomCharacterTypes[random]();
        numberOfCharacters--;
    }
    return password;
}

randomInt 可以简化为返回 '0''9' 之间的字符,与您的其他函数相同:

char randomInt(){
    return '0' + rand() % 10;
}

如果您使用字符而不是数字,则您的其他函数更具可读性,然后您必须添加注释以提醒您它们代表哪个字符:

char randomLowerCharacter(){
    return 'a' + rand()%26;
}
char randomCapitalCharacter(){
    return 'A' + rand()%26;
}
char randomSpecialCharacter(){
    /** Special characters are split by numbers so we pick a random from one of the 2 groups.*/
    return (rand()%2) ? char('!' + rand()%14) : char(':' + rand()%8); 
}

请注意,rand() % x 不是一个好的随机数生成器,您应该改用标准库随机函数(尤其是当您将这些密码用于任何重要的事情时)。

您的代码可以使用strings 进一步简化以包含您的字符类:

#include <string>
#include <string_view>
#include <iostream>
#include <random>
#include <array>

class PasswordGenerator
{
public:
    PasswordGenerator()
    {
        for (size_t i = 0; i < characterTypes.size(); i++)
        {
            characterTypeDistributions[i] = std::uniform_int_distribution<int>(0, characterTypes[i].size() - 1);
        }
    }

    static constexpr std::array<std::string_view, 4> characterTypes = {
            "abcdefghijklmnopqrstuvwxyz",
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
            "0123456789",
            "!\"#$%&'()*+,-./:;<=>?@"
        };
    std::array<std::uniform_int_distribution<int>, characterTypes.size()> characterTypeDistributions;
    std::uniform_int_distribution<int> typeDistribution{0, characterTypes.size() - 1};
    std::mt19937 eng{std::random_device{}()};

    std::string generate(int numberOfCharacters){
        std::uniform_int_distribution<int> dist{0, 5};
        std::string password;
        password.reserve(numberOfCharacters);
        for (int i = 0; i < numberOfCharacters; i++)
        {
            auto type = typeDistribution(eng);
            password += characterTypes[type][characterTypeDistributions[type](eng)];
        }
        return password;
    }
};

int main(){
    int passwordLength = 12;
    PasswordGenerator gen;
    std::string password = gen.generate(passwordLength);
    std::cout << password << "\n";
    return 0;
}

请注意,此代码可能仍不符合生成密码的高安全标准。

【讨论】:

  • 至少需要 600 个 MT19937 输出,才有很大机会恢复其状态,您在此处远低于此状态。也就是说,最好只使用 random_device 作为引擎而不是使用 mt19937
  • @SamMason 取决于random_device 的质量...
  • 这确实是标准允许的,但我很想知道在哪些实际实现中这实际上会导致密码难以预测。例如在带有任何主流编译器的 linux、osx 和 windows 下我当然不想涉及mt19937
【解决方案2】:

感谢@RetiredNinja 指出问题不在于 srand() 而在于我的数组中的函数调用不当。使用 switch 语句来正确调用函数解决了这个问题。

//improper calling of functions in an array. Resolved with switch statement below
//char randomCharacterTypes[4] = {randomLowerCharacter(),randomInt(), randomCapitalCharacter(), randomSpecialCharacter()};

while (numberOfCharacters > 0){
    int random = rand()%4;
    switch(random){
    case 0:
        password += randomLowerCharacter();
        break;
    case 1:
        password += randomInt();
        break;
    case 2:
        password += randomCapitalCharacter();
        break;
    case 3:
        password += randomSpecialCharacter();
        break;
    }
    numberOfCharacters--;
}

【讨论】:

  • 您可以通过在循环体末尾删除减量并将其合并到条件中来简化循环:while(--numberOfCharacters &gt;= 0) {...}
猜你喜欢
  • 1970-01-01
  • 2012-11-02
  • 2016-07-13
  • 1970-01-01
  • 2022-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-08
相关资源
最近更新 更多