Як генерувати випадкове число в C ++?


150

Я намагаюся скласти гру з кубиками, і мені потрібно мати в ній випадкові числа (для імітації сторін штампу. Я знаю, як зробити це між 1 і 6). Використання

#include <cstdlib> 
#include <ctime> 
#include <iostream>

using namespace std;

int main() 
{ 
    srand((unsigned)time(0)); 
    int i;
    i = (rand()%6)+1; 
    cout << i << "\n"; 
}

не дуже добре, тому що коли я кілька разів запускаю програму, ось такий результат я отримую:

6
1
1
1
1
1
2
2
2
2
5
2

Тому я хочу команду, яка буде генерувати різні випадкові числа кожного разу, а не одне і те саме 5 разів підряд. Чи є команда, яка це зробить?


55
Проблеми з розподілом осторонь, майте на увазі, що при випадкових числах виникає можливість отримання одного і того ж результату кілька разів поспіль. Якби ви гарантували, що не отримаєте однакове число двічі поспіль, результати насправді не були б випадковими, чи не так?
cdhowie

5
Чому ви вважаєте, що ці числа не випадкові? Киньте штамп по-справжньому, і ви дуже добре зможете отримати такий результат. Якби вони гарантували різницю між кожним кидком, то це насправді не було б випадково.
mattjgalloway

2
Читайте також foreverlyconfuzzled.com/arts/jsw_art_rand.aspx, чому використання оператора модуля часто не є хорошою ідеєю.
Бенджамін Баньє

4
Ви нерозумієте набагато більше, ніж один може вмістити коментар або навіть відповідь. Вам потрібно самостійно дізнатися про генератори псевдовипадкових чисел, про насіння, про важливість вибору справді випадкового насіння та про рівномірне розподіл.
Керрек СБ

20
Коли ви посівете з часом. Це також означає, що якщо ви запускаєте свою програму більше одного разу на секунду, ви отримаєте однакове число.
Мартін Йорк

Відповіді:


79

Найголовніша проблема вашої заявки на тестування полягає в тому, що ви телефонуєте srandодин раз, а потім телефонуєте randодин раз і виходите.

Вся суть srandфункції полягає в ініціалізації послідовності псевдовипадкових чисел з випадковим початком.

Це означає , що якщо ви передаєте те ж значення для srandдвох різних додатків (з тієї ж srand/ randреалізацією) , то ви отримаєте таку саму послідовність з rand()лічених значень після цього в обох додатках.

Однак у вашому прикладі програми псевдовипадкова послідовність складається лише з одного елемента - першого елемента псевдовипадкової послідовності, сформованого з насіння, рівного поточному часу secondточності. Що ви очікуєте побачити на виході?

Очевидно, коли вам трапляється запускати програму в одну і ту ж секунду - ви використовуєте одне і те ж значення насіння - таким чином, ваш результат, звичайно, такий же (як уже згадував Мартін Йорк у коментарі до питання).

Насправді вам слід зателефонувати srand(seed)один раз, а потім зателефонувати rand() багато разів та проаналізувати цю послідовність - це має виглядати випадково.

Редагувати:

О, я розумію. Мабуть, словесного опису недостатньо (можливо, мовний бар'єр чи щось ... :)).

ГАРАЗД. Приклад старомодного коду С на основі тих же srand()/rand()/time()функцій, що і в запитанні:

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

int main(void)
{
    unsigned long j;
    srand( (unsigned)time(NULL) );

    for( j = 0; j < 100500; ++j )
    {
        int n;

        /* skip rand() readings that would make n%6 non-uniformly distributed
          (assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
        while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
        { /* bad value retrieved so get next one */ }

        printf( "%d,\t%d\n", n, n % 6 + 1 );
    }

    return 0;
}

^^^ ТАЯ послідовність з одного запуску програми повинна виглядати випадково.

EDIT2:

Використовуючи стандартну бібліотеку C або C ++, важливо розуміти, що на сьогоднішній день не існує єдиної стандартної функції або класу, який би остаточно давав фактичні випадкові дані (це гарантується стандартом). Єдиним стандартним інструментом, який підходить до цієї проблеми, є std :: random_device, який, на жаль, все ще не дає гарантій фактичної випадковості.

Залежно від характеру програми, слід спочатку вирішити, чи справді вам потрібні справді випадкові (непередбачувані) дані. Примітним випадком, коли вам справді потрібна справжня випадковість, є захист інформації - наприклад, генерування симетричних ключів, асиметричних приватних ключів, значень солі, жетонів безпеки тощо.

Однак випадкові числа класів безпеки - це окрема галузь, яка вартує окремої статті.

У більшості випадків достатньо генератора псевдовипадкових чисел - наприклад, для наукових симуляцій чи ігор. У деяких випадках навіть потрібна послідовно визначена псевдовипадкова послідовність - наприклад, в іграх ви можете генерувати точно такі ж карти під час виконання, щоб уникнути зберігання великої кількості даних.

Первісне запитання та повторюваність безлічі однакових / подібних питань (і навіть багато помилкових "відповідей" на них) вказують на те, що перш за все важливо відрізнити випадкові числа від псевдовипадкових чисел І зрозуміти, що таке послідовність псевдовипадкових чисел у перше місце ТА зрозуміти, що генератори псевдовипадкових чисел НЕ використовуються так само, як ви могли використовувати справжні генератори випадкових чисел.

Інтуїтивно, коли ви запитуєте випадкове число - результат, що повертається, не повинен залежати від раніше повернених значень і не повинен залежати, якщо хтось щось запитував раніше, і не повинен залежати в який момент і яким процесом, на якому комп'ютері і на якому генераторі та в якому яку галактику вона запитувала. Ось що слово "випадковий" означає все-таки - бути непередбачуваним і незалежним ні від чого - інакше воно вже не випадкове, правда? З цією інтуїцією цілком природно шукати в Інтернеті певні магічні заклинання, щоб отримати таке випадкове число в будь-якому можливому контексті.

^^^ ТАКІ інтуїтивні очікування ДУЖЕ НЕПРАВИЛЬНІ та шкідливі у всіх випадках із псевдо-випадковими генераторами чисел - незважаючи на те, що розумні для справжніх випадкових чисел.

Хоча змістовне поняття "випадкове число" існує - немає такого поняття, як "псевдовипадкове число". Псевдо-генератор випадкових чисел на насправді виробляє псевдовипадкових чисел послідовності .

Коли експерти говорять про якість PRNG, вони насправді говорять про статистичні властивості генерованої послідовності (та її помітних підрядів). Наприклад, якщо ви поєднуєте два високоякісні PRNG, використовуючи їх обидва по черзі - ви можете створити погану послідовність результатів, - незважаючи на те, що вони генерують хороші послідовності кожна окремо (ці дві хороші послідовності можуть просто співвідноситися між собою і таким чином погано поєднуватись).

Псевдовипадкова послідовність насправді завжди детермінована (заздалегідь визначена її алгоритмом та початковими параметрами), тобто насправді в цьому немає нічого випадкового.

Конкретно rand()/ srand(s)пара функцій забезпечує сингулярну псевдовипадкову псевдовипадкову послідовність чисел (!) Для кожного процесу, що генерується за допомогою алгоритму, визначеного реалізацією. Функція rand()створює значення в діапазоні [0, RAND_MAX].

Цитата від стандарту C11:

srandФункція використовує аргумент в якості затравки для нової послідовності псевдовипадкових чисел , які будуть повернуті при наступних викликах rand. Якщо srandпотім викликається однакове значення насіння, послідовність псевдовипадкових чисел повторюється. Якщо randвиклик перед тим, srandяк були здійснені будь-які дзвінки , формується та сама послідовність, що і коли srandвперше викликається із початковим значенням 1.

Багато людей , розумно очікувати , що rand()буде виробляти послідовність напівзалежними рівномірно розподілених чисел в діапазоні 0до RAND_MAX. Ну, це, безумовно, повинно (інакше це марно), але, на жаль, не тільки стандарт цього не вимагає - є навіть явна відмова від відповідальності, яка стверджує, що "немає гарантій якості виробленої випадкової послідовності" . У деяких історичних випадках rand/ srandреалізація була дуже поганої якості на самому справі. Навіть незважаючи на те, що в сучасних реалізаціях це, швидше за все, досить добре - але довіра порушена і відновити її непросто. Окрім того, що його безпечний характер робить його безпечним використання у багатопотокових програмах складним та обмеженим (все-таки можливо - ви можете просто використовувати їх з однієї спеціальної теми).

Новий шаблон класу std :: mersenne_twister_engine <> (та його зручність typedefs - std::mt19937/ std::mt19937_64з хорошою комбінацією параметрів шаблону) забезпечує генератор псевдовипадкових чисел на об'єкт, визначений у стандарті C ++ 11. З однаковими параметрами шаблону та однаковими параметрами ініціалізації різні об'єкти будуть генерувати абсолютно однакову послідовність виводу на один об'єкт на будь-якому комп'ютері будь-якого додатка, побудованого зі стандартною бібліотекою, що відповідає C ++ 11. Перевагою цього класу є його передбачувано висока якість вихідної послідовності та повна узгодженість між реалізаціями.

Також є більше двигунів PRNG, визначених у стандарті C ++ 11 - std :: linear_congruential_engine <> (історично використовується як srand/randалгоритм справедливої ​​якості в деяких реалізаціях стандартної бібліотеки C) та std :: subtract_with_carry_engine <> . Вони також генерують повністю визначені параметри, що залежать від виводу послідовностей на об'єкт.

Приклад заміни сучасного C ++ 11 для застарілого коду С вище:

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    // seed value is designed specifically to make initialization
    // parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
    // different across executions of application
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);

    for( unsigned long j = 0; j < 100500; ++j )
    /* ^^^Yes. Generating single pseudo-random number makes no sense
       even if you use std::mersenne_twister_engine instead of rand()
       and even when your seed quality is much better than time(NULL) */    
    {
        std::mt19937::result_type n;
        // reject readings that would make n%6 non-uniformly distributed
        while( ( n = gen() ) > std::mt19937::max() -
                                    ( std::mt19937::max() - 5 )%6 )
        { /* bad value retrieved so get next one */ }

        std::cout << n << '\t' << n % 6 + 1 << '\n';
    }

    return 0;
}

Версія попереднього коду, що використовує std :: uniform_int_distribution <>

#include <iostream>
#include <chrono>
#include <random>

int main()
{
    std::random_device rd;
    std::mt19937::result_type seed = rd() ^ (
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::seconds>(
                std::chrono::system_clock::now().time_since_epoch()
                ).count() +
            (std::mt19937::result_type)
            std::chrono::duration_cast<std::chrono::microseconds>(
                std::chrono::high_resolution_clock::now().time_since_epoch()
                ).count() );

    std::mt19937 gen(seed);
    std::uniform_int_distribution<unsigned> distrib(1, 6);

    for( unsigned long j = 0; j < 100500; ++j )
    {
        std::cout << distrib(gen) << ' ';
    }

    std::cout << '\n';
    return 0;
}

Я попросив подібне запитання за посиланням тут, але все ще не міг знайти жодної чіткої відповіді. Чи можете ви продемонструвати, будь ласка, "Насправді ви повинні зателефонувати srand (seed) один раз, а потім викликати rand ()" з кодами, тому що я вже зробив те, що ви говорите, але це не працює належним чином.
башбурак

2
@bashburak Здається, ви повністю пропустили суть цієї відповіді. Чому саме ти вирізав мою цитату? У своїй відповіді я буквально сказав: "Насправді ви повинні зателефонувати srand (seed) один раз, а потім зателефонувати rand () багато разів і проаналізувати цю послідовність - вона повинна виглядати випадково". Ви помітили, що вам слід зателефонувати rand () МНОГО ЧАСУ після виклику одного srand (...)? Ваше запитання у вашому посиланні є точним дублікатом цього питання з точно таким же непорозумінням.
Серж Дундич

Це стара відповідь, але вона з’являється, коли ви перебуваєте в Google "С ++ генерація випадкових чисел". Це погана порада для програмістів на C ++, оскільки він радить використовувати rand()та srand(). Чи можете ви її оновити?
Якк - Адам Невраумон

@ Yakk-AdamNevraumont Насправді не радить використовувати rand()і srand(). Насправді він просто відповідає на питання із наданим описом. З опису (що використовує rand/ srand) видно, що слід пояснити основні поняття генерації псевдовипадкових чисел - як саме значення псевдовипадкової послідовності та її насіння. Я намагаюся зробити саме це і використовувати найпростіші та знайомі rand/ srandкомбінації. Найсмішніше, що деякі інші відповіді - навіть з дуже великим рейтингом - страждають від тих же непорозумінь, що і автор запитання.
Серж Дундич

@ Yakk-AdamNevraumont Я взяв вашу пораду та доповнив свою відповідь деякою інформацією про новітні доповнення C ++. Хоча я вважаю , що це трохи не по темі - але ваша пропозиція, а також деякі інші відповіді вказує на те, що обидва хороші старі std::rand/std::srandі нові C ++ бібліотека функцій , таких як std::random_device<>, станд :: mersenne_twister_engine <> і безліч випадкових розподілів вимагають деякого пояснення.
Серж Дундич

216

Використання модуля може ввести зміщення у випадкові числа, залежно від генератора випадкових чисел. Дивіться це питання для отримання додаткової інформації. Звичайно, цілком можливо отримати повторювані числа у випадковій послідовності.

Спробуйте кілька функцій C ++ 11 для кращого розповсюдження:

#include <random>
#include <iostream>

int main()
{
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]

    std::cout << dist6(rng) << std::endl;
}

Дивіться це запитання / відповідь для отримання додаткової інформації про випадкові числа C ++ 11. Сказане - не єдиний спосіб зробити це, але це один із способів.


7
Кількість упередженостей, введених за допомогою використання %6, зникає на малому рівні. Можливо, важливо, якщо ви пишете гру про кості, яка буде використовуватися в Лас-Вегасі, але без наслідків майже в будь-якому іншому контексті.
Гарячий лизає

9
HotLicks: погоджено, але якщо ви використовуєте версію C ++, яка підтримує random_deviceі mt19937вже є, буквально немає причин не виходити з роботи і використовувати стандарт uniform_int_distributionтакож.
Quuxplusone

4
Усі програмісти повинні порадити людям уникати модуля, як чума, оскільки він використовує поділ, і це коштує сотні годинних циклів і може зіпсувати терміни вашої програми та / або спалити багато енергії акумулятора.

3
Rng для "діапазону"?
Крістофер

4
@ ChristofferHjärtström: Це для г andom п умбра г enerator.
Cornstalks

11

Якщо ви використовуєте boost libs, ви можете отримати випадковий генератор таким чином:

#include <iostream>
#include <string>

// Used in randomization
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>

using namespace std;
using namespace boost;

int current_time_nanoseconds(){
    struct timespec tm;
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_nsec;
}

int main (int argc, char* argv[]) {
    unsigned int dice_rolls = 12;
    random::mt19937 rng(current_time_nanoseconds());
    random::uniform_int_distribution<> six(1,6);

    for(unsigned int i=0; i<dice_rolls; i++){
        cout << six(rng) << endl;
    }
}

Де функція current_time_nanoseconds()задає поточний час у наносекундах, який використовується в якості насіння.


Ось більш загальний клас для отримання випадкових цілих чисел і дат у діапазоні:

#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"


using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;


class Randomizer {
private:
    static const bool debug_mode = false;
    random::mt19937 rng_;

    // The private constructor so that the user can not directly instantiate
    Randomizer() {
        if(debug_mode==true){
            this->rng_ = random::mt19937();
        }else{
            this->rng_ = random::mt19937(current_time_nanoseconds());
        }
    };

    int current_time_nanoseconds(){
        struct timespec tm;
        clock_gettime(CLOCK_REALTIME, &tm);
        return tm.tv_nsec;
    }

    // C++ 03
    // ========
    // Dont forget to declare these two. You want to make sure they
    // are unacceptable otherwise you may accidentally get copies of
    // your singleton appearing.
    Randomizer(Randomizer const&);     // Don't Implement
    void operator=(Randomizer const&); // Don't implement

public:
    static Randomizer& get_instance(){
        // The only instance of the class is created at the first call get_instance ()
        // and will be destroyed only when the program exits
        static Randomizer instance;
        return instance;
    }
    bool method() { return true; };

    int rand(unsigned int floor, unsigned int ceil){
        random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
        return (rand_(rng_));
    }

    // Is not considering the millisecons
    time_duration rand_time_duration(){
        boost::posix_time::time_duration floor(0, 0, 0, 0);
        boost::posix_time::time_duration ceil(23, 59, 59, 0);
        unsigned int rand_seconds = rand(floor.total_seconds(), ceil.total_seconds());
        return seconds(rand_seconds);
    }


    date rand_date_from_epoch_to_now(){
        date now = second_clock::local_time().date();
        return rand_date_from_epoch_to_ceil(now);
    }

    date rand_date_from_epoch_to_ceil(date ceil_date){
        date epoch = ptime(date(1970,1,1)).date();
        return rand_date_in_interval(epoch, ceil_date);
    }

    date rand_date_in_interval(date floor_date, date ceil_date){
        return rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
    }

    ptime rand_ptime_from_epoch_to_now(){
        ptime now = second_clock::local_time();
        return rand_ptime_from_epoch_to_ceil(now);
    }

    ptime rand_ptime_from_epoch_to_ceil(ptime ceil_date){
        ptime epoch = ptime(date(1970,1,1));
        return rand_ptime_in_interval(epoch, ceil_date);
    }

    ptime rand_ptime_in_interval(ptime floor_date, ptime ceil_date){
        time_duration const diff = ceil_date - floor_date;
        long long gap_seconds = diff.total_seconds();
        long long step_seconds = Randomizer::get_instance().rand(0, gap_seconds);
        return floor_date + seconds(step_seconds);
    }
};

1
Тепер, коли у нас є випадковість, як частина стандарту, я б відмовив від використання boost-версії, якщо ви не використовуєте справді старий компілятор.
Мартін Йорк,

9
#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
    srand(time(NULL));
    int random_number = std::rand(); // rand() return a number between ​0​ and RAND_MAX
    std::cout << random_number;
    return 0;
}

http://en.cppreference.com/w/cpp/numeric/random/rand


Чим відрізняється авторський код питання? (За винятком того, що ви не використовуєте %6.) А якщо ви вирішили використовувати std::randC ++ API randфункції бібліотеки C, то чому б не використовувати std::timeі std::srandзаради послідовності стилю C ++?
Серж Дундич

4

Тут можна отримати повний Randomerкод класу для генерації випадкових чисел!

Якщо вам потрібні випадкові числа в різних частинах проекту, ви можете створити окремий клас, Randomerщоб інкапсулювати всі randomречі всередині нього.

Щось схоже:

class Randomer {
    // random seed by default
    std::mt19937 gen_;
    std::uniform_int_distribution<size_t> dist_;

public:
    /*  ... some convenient ctors ... */ 

    Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
        : gen_{seed}, dist_{min, max} {
    }

    // if you want predictable numbers
    void SetSeed(unsigned int seed) {
        gen_.seed(seed);
    }

    size_t operator()() {
        return dist_(gen_);
    }
};

Такий клас буде корисний пізніше:

int main() {
    Randomer randomer{0, 10};
    std::cout << randomer() << "\n";
}

Ви можете перевірити це посилання як приклад, як я використовую такий Randomerклас для генерації випадкових рядків. Ви також можете використовувати, Randomerякщо хочете.


Ви не хочете повторно використовувати генератор для всіх своїх об'єктів Randomer? Тим більше, що створити ініціалізувати та підтримувати свою державу порівняно дорого.
Мартін Йорк,

3

Кожен раз генеруйте різне випадкове число, не одне і те ж шість разів підряд.

Використовуйте сценарій випадку

Я порівнював проблему передбачуваності з мішком з шістьма бітами паперу, на кожному з яких було написано значення від 0 до 5. Шматок паперу витягується з мішка щоразу, коли потрібно нове значення. Якщо сумка порожня, то номери кладуть назад у мішок.

... з цього я можу створити алгоритм сортування.

Алгоритм

Сумка зазвичай є Collection. Я вибрав bool[](інакше відомий як булева масив, бітова площина або бітова карта), щоб взяти на себе роль сумки.

Я обрав причину bool[]тому, що індекс кожного елемента вже є значенням кожного аркуша паперу. Якби папери вимагали нічого іншого, написаного на них, я б використав Dictionary<string, bool>її замість. Булеве значення використовується для того, щоб відслідковувати, чи було виведено число чи ні.

Лічильник, що викликається RemainingNumberCount, ініціалізується на 5той, який відлічується як обране випадкове число Це позбавляє нас від необхідності підраховувати кількість аркушів паперу, що залишається кожного разу, коли ми хочемо намалювати нове число.

Для того, щоб вибрати наступну випадкову величину Я використовую for..loopдля сканування через мішок індексів, і лічильник відрахувати , коли indexце falseназивається NumberOfMoves.

NumberOfMovesвикористовується для вибору наступного доступного номера. NumberOfMovesспочатку встановлюється як випадкове значення між 0і 5, оскільки є 0..5 доступних кроків, які ми можемо зробити через сумку. На наступній ітерації NumberOfMovesвстановлюється випадкове значення між 0і 4, оскільки зараз є 0..4 кроки, які ми можемо зробити через сумку. Коли цифри використовуються, наявні числа зменшуються, тому ми замість цього використовуємо rand() % (RemainingNumberCount + 1)для обчислення наступного значення для NumberOfMoves.

Коли NumberOfMovesлічильник досягне нуля, for..loopслід наступним чином:

  1. Встановіть поточне значення таким самим, як for..loopі індекс 's.
  2. Встановіть усі цифри в сумці на false.
  3. Перерва від for..loop.

Код

Код для вищезгаданого рішення такий:

(Поставте наступні три блоки в основний .cpp-файл один за одним)

#include "stdafx.h"
#include <ctime> 
#include <iostream>
#include <string>

class RandomBag {
public:
    int Value = -1;

    RandomBag() {
        ResetBag();

    }

    void NextValue() {
        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        int NumberOfMoves = rand() % (RemainingNumberCount + 1);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            if (BagOfNumbers[i] == 0) {
                NumberOfMoves--;

                if (NumberOfMoves == -1)
                {
                    Value = i;

                    BagOfNumbers[i] = 1;

                    break;

                }

            }



        if (RemainingNumberCount == 0) {
            RemainingNumberCount = 5;

            ResetBag();

        }
        else            
            RemainingNumberCount--; 

    }

    std::string ToString() {
        return std::to_string(Value);

    }

private:
    bool BagOfNumbers[6]; 

    int RemainingNumberCount;

    int NumberOfMoves;

    void ResetBag() {
        RemainingNumberCount = 5;

        NumberOfMoves = rand() % 6;

        int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);

        for (int i = 0; i < BagOfNumbersLength; i++)            
            BagOfNumbers[i] = 0;

    }

};

Консольний клас

Я створюю цей клас консолі, оскільки це дозволяє легко перенаправляти вихід.

Нижче в коді ...

Console::WriteLine("The next value is " + randomBag.ToString());

... можна замінити на ...

std::cout << "The next value is " + randomBag.ToString() << std::endl; 

... і тоді цей Consoleклас за бажанням можна видалити.

class Console {
public:
    static void WriteLine(std::string s) {
        std::cout << s << std::endl;

    }

};

Основний метод

Приклад використання наступним чином:

int main() {
    srand((unsigned)time(0)); // Initialise random seed based on current time

    RandomBag randomBag;

    Console::WriteLine("First set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nSecond set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nThird set of six...\n");

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    randomBag.NextValue();

    Console::WriteLine("The next value is " + randomBag.ToString());

    Console::WriteLine("\nProcess complete.\n");

    system("pause");

}

Приклад виведення

Коли я запустив програму, я отримав такий результат:

First set of six...

The next value is 2
The next value is 3
The next value is 4
The next value is 5
The next value is 0
The next value is 1

Second set of six...

The next value is 3
The next value is 4
The next value is 2
The next value is 0
The next value is 1
The next value is 5

Third set of six...

The next value is 4
The next value is 5
The next value is 2
The next value is 0
The next value is 3
The next value is 1

Process complete.

Press any key to continue . . .

Заключна заява

Ця програма була написана за допомогою Visual Studio 2017 , і я вирішив зробити її Visual C++ Windows Console Applicationпроектом за допомогою .Net 4.6.1.

Я тут не роблю нічого особливого, тому код повинен працювати і на більш ранніх версіях Visual Studio.


Якщо це VS 2017, ви повинні використовувати останню версію стандартної бібліотеки: en.cppreference.com/w/cpp/numeric/random . В даний час у цьому прикладі використовуються функції випадкової бібліотеки С та "Не існує гарантій якості створеної випадкової послідовності".
Роберт Анджеюк

3

Кожен раз, коли ви здійснюєте основний пошук random number generationв Інтернеті на мові програмування на C ++, це питання, як правило, першим з’являється! Я хочу кинути капелюх на ринг, щоб, сподіваюся, краще прояснити концепцію генерації псевдовипадкових чисел у С ++ для майбутніх кодерів, які неминуче шукають це те саме питання в Інтернеті!

Основи

Генерація псевдовипадкових чисел включає процес використання детермінованого алгоритму, який виробляє послідовність чисел, властивості яких приблизно нагадують випадкові числа . Я кажу приблизно схоже , тому що справжня випадковість є досить невловимою загадкою математики та інформатики. Отже, чому термін псевдовипадковий використовується для більш педантично правильного!

Перш ніж реально використовувати PRNG, тобто, pseudo-random number generatorви повинні надати алгоритму початкове значення, яке часто також називається початковим . Однак насіння потрібно встановити лише один раз перед використанням самого алгоритму!

/// Proper way!
seed( 1234 ) /// Seed set only once...
for( x in range( 0, 10) ):
  PRNG( seed ) /// Will work as expected

/// Wrong way!
for( x in rang( 0, 10 ) ):
  seed( 1234 ) /// Seed reset for ten iterations!
  PRNG( seed ) /// Output will be the same...

Таким чином, якщо ви хочете гарної послідовності чисел, тоді ви повинні надати достатнє насіння в PRNG!

Старий шлях С

Зворотна сумісна стандартна бібліотека C, яку має C ++, використовує те, що називається лінійним конгрурентним генератором, знайденим у cstdlibфайлі заголовка! Цей PRNG функціонує за допомогою переривчастої кусочної функції, яка використовує модульну арифметику, тобто швидкий алгоритм, який любить використовувати modulo operator '%'. Нижче наведено загальне використання цього PRNG, що стосується оригінального запитання, заданого @Predictability:

#include <iostream>
#include <cstdlib>
#include <ctime>

int main( void )
{
  int low_dist  = 1;
  int high_dist = 6;
  std::srand( ( unsigned int )std::time( nullptr ) );
  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << low_dist + std::rand() % ( high_dist - low_dist ) << std::endl;
  return 0;
}

Загальне використання PRNG C займає цілу низку питань, таких як:

  1. Загальний інтерфейс std::rand()не дуже інтуїтивно зрозумілий для правильного генерування псевдовипадкових чисел між заданим діапазоном, наприклад, отримання чисел між [1, 6] так, як хотіла @Predictability.
  2. Загальне використання std::rand()виключає можливість рівномірного розподілу псевдовипадкових чисел, завдяки принципу Голуба .
  3. Звичайний спосіб std::rand()висіву насіння через std::srand( ( unsigned int )std::time( nullptr ) )технічно не є правильним, оскільки time_tвважається обмеженим типом . Тому перехід від time_tдо unsigned int не гарантується!

Для отримання більш детальної інформації про загальні проблеми використання PRNG C та як можливо їх обійти, зверніться до розділу Використання rand () (C / C ++): Поради щодо функції rand () стандартної бібліотеки C !

Стандартний шлях C ++

З моменту опублікування стандарту ISO / IEC 14882: 2011, тобто C ++ 11, randomбібліотека вже деякий час є окремо від мови програмування C ++. Ця бібліотека оснащена декількома PRNG та різними типами дистрибуції, такими як: рівномірний розподіл , нормальний розподіл , біноміальний розподіл тощо. Наступний приклад вихідного коду демонструє дуже базове використання randomбібліотеки, що стосується початкового питання @ Predictability:

#include <iostream>
#include <cctype>
#include <random>

using u32    = uint_least32_t; 
using engine = std::mt19937;

int main( void )
{
  std::random_device os_seed;
  const u32 seed = os_seed();

  engine generator( seed );
  std::uniform_int_distribution< u32 > distribute( 1, 6 );

  for( int repetition = 0; repetition < 10; ++repetition )
    std::cout << distribute( generator ) << std::endl;
  return 0;
}

32-бітний Вихор Мерсенна двигун, з рівномірним розподілом по цілочисельних значень був використаний в наведеному вище прикладі. (Назва двигуна у вихідному коді звучить дивно, оскільки його назва походить з періоду 2 ^ 19937-1). Приклад також використовує std::random_deviceдля виведення двигуна, який отримує своє значення з операційної системи (Якщо ви використовуєте систему Linux, то std::random_deviceповертаєте значення з /dev/urandom).

Зверніть увагу, що вам не доведеться використовувати std::random_deviceдля засіву жодного двигуна . Ви можете використовувати константи або навіть chronoбібліотеку! Вам також не доведеться використовувати 32-бітну версію std::mt19937двигуна, є й інші варіанти ! Щоб отримати докладнішу інформацію про можливості randomбібліотеки, зверніться до cplusplus.com

Загалом, програмісти на C ++ std::rand()більше не повинні використовувати не тому, що це погано , а тому, що чинний стандарт забезпечує кращі альтернативи, які є більш прямими та надійними . Сподіваємось, багато хто з вас вважають це корисним, особливо тих, хто нещодавно шукав в Інтернеті generating random numbers in c++!


2

Ось рішення. Створіть функцію, яка повертає випадкове число і поставте її за межі основної функції, щоб зробити її глобальною. Сподіваюся, це допомагає

#include <iostream>
#include <cstdlib>
#include <ctime>
int rollDie();
using std::cout;
int main (){
    srand((unsigned)time(0));
    int die1;
    int die2;
    for (int n=10; n>0; n--){
    die1 = rollDie();
    die2 = rollDie();
    cout << die1 << " + " << die2 << " = " << die1 + die2 << "\n";
}
system("pause");
return 0;
}
int rollDie(){
    return (rand()%6)+1;
}

2

Цей код виробляє випадкові числа від nдо m.

int random(int from, int to){
    return rand() % (to - from + 1) + from;
}

приклад:

int main(){
    srand(time(0));
    cout << random(0, 99) << "\n";
}

2
Це насправді не відповідає на питання.
HolyBlackCat

1
Ви цього не виправили. Суть питання полягає в тому, що якщо ви запускаєте програму кілька разів в секунду, то вона генерує однакові випадкові значення. Ваш код теж робить це.
HolyBlackCat

1
@HolyBlackCat Я перевірив це на кілька запусків, він працює. Ви раніше додавали srand(time(0))до головної функції random(n, m)?
Амір Фо

1
Ви повинні додати srand(time(0))до основної функції не для циклу або всередині реалізації функції.
Амір Фо

1
Я переписав ваш код дослівно. Ви запускали його кілька разів на секунду ?
HolyBlackCat

1

для випадкового кожного файлу RUN

size_t randomGenerator(size_t min, size_t max) {
    std::mt19937 rng;
    rng.seed(std::random_device()());
    //rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
    std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);

    return dist(rng);
}

1
Ви не повинні створювати генератор кілька разів. Він підтримує купу стану, щоб він генерував послідовність випадкових чисел, що мають відповідний розподіл (щоб він виглядав випадковим).
Мартін Йорк,

-2

Ось простий випадковий генератор з прибл. однакова ймовірність отримання позитивних і негативних значень приблизно 0:

  int getNextRandom(const size_t lim) 
  {
        int nextRand = rand() % lim;
        int nextSign = rand() % lim;
        if (nextSign < lim / 2)
            return -nextRand;
        return nextRand;
  }


   int main()
   {
        srand(time(NULL));
        int r = getNextRandom(100);
        cout << r << endl;
        return 0;
   }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.