Как мне получить два разных случайных числа в одном и том же семени srand ()? C ++

Я пытаюсь создать генератор случайных паролей. Когда я вызываю функцию несколько раз, она возвращает одно и то же значение символа. QQpQ;7Q7pQ;p

Я пробовал добавить srand(time(0)); или srand((unsigned int) time(NULL)); в свою основную функцию.

Глядя на другие сообщения stack overflow, я вижу, что 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); 
}

person mfischercodes    schedule 13.04.2021    source источник
comment
randomCharacterTypes[4] содержит только 4 разных символа, вы хотели создать массив указателей на функции?   -  person Alan Birtles    schedule 13.04.2021
comment
Когда я запускаю ваш код, он в первый раз печатает RReR>>eReR6e, а второй раз - g>g>7gTgg>>T. Это потому, что вы запускаете его более одного раза за одну и ту же секунду? Это даст те же значения, потому что time (NULL) возвращает целое число секунд, поэтому, если вы запустите его более одного раза в один и тот же второй раз (NULL), вернет то же значение.   -  person Jerry Jeremiah    schedule 13.04.2021
comment
Поскольку вы используете c ++ 11, вам следует отказаться от srand в пользу новых генераторов случайных чисел: stackoverflow.com/a/66850447 / 1294207   -  person Fantastic Mr Fox    schedule 13.04.2021
comment
@AlanBirtles О, понятно, да, я пытаюсь использовать указатели на функции. Я вижу это, если просто вызову randomLowerCharacter (); что он действительно возвращает случайное символьное значение. Я посмотрю, как реализовать указатели на функции. Спасибо!!!   -  person mfischercodes    schedule 13.04.2021
comment
Вам, вероятно, будет лучше использовать оператор switch после int random = rand()%4;, чтобы вызвать правильную функцию и получить символ. Сейчас вы вызываете каждую функцию только один раз и сохраняете результат в randomCharacterTypes. Похоже, вы надеетесь, что каждый раз вызываете функцию, но это не так, и переключение может иметь больше смысла, чем попытки объяснить, как работают указатели на функции.   -  person Retired Ninja    schedule 13.04.2021
comment
Вы можете избежать необходимости в комментариях типа //97 = a, если вы напишете 'a' вместо 97 в коде (и аналогично для других символов).   -  person molbdnilo    schedule 13.04.2021
comment
@RetiredNinja Отлично, спасибо, что прекрасно работает. Я по-прежнему изучу указатели на функции, так как думаю, что это будет выглядеть чище. Но пока оператор switch заставляет мою программу работать так, как задумано. Спасибо!!   -  person mfischercodes    schedule 13.04.2021
comment
Не вкладывайте ответы в свой вопрос. Вместо этого напишите правильный ответ. Я откатился к вашему первоначальному вопросу.   -  person Ted Lyngmo    schedule 13.04.2021


Ответы (2)


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;
}

Обратите внимание, что этот код может по-прежнему не соответствовать высоким стандартам безопасности для генерации паролей.

person Alan Birtles    schedule 13.04.2021
comment
необходимо как минимум 600 выходов MT19937, прежде чем появится большая вероятность восстановления его состояния, о чем вы здесь ниже. при этом, вероятно, лучше использовать исключительно random_device для движка, а не использовать mt19937 вообще - person Sam Mason; 13.04.2021
comment
@SamMason зависит от качества _1 _... - person Alan Birtles; 13.04.2021
comment
это действительно разрешено стандартом, но я был бы заинтригован, узнав, в каких реальных реализациях это на самом деле приведет к менее предсказуемому паролю. например под linux, osx и windows с любым основным компилятором я бы, конечно, предпочел не задействовать mt19937 - person Sam Mason; 13.04.2021

Спасибо @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--;
}
person mfischercodes    schedule 13.04.2021
comment
Вы можете немного упростить цикл, отбросив декремент в конце тела цикла и вместо этого включив его в условие: while(--numberOfCharacters >= 0) {...} - person CiaPan; 13.04.2021