Hasilkan nomor acak menggunakan pustaka acak C ++ 11


143

Seperti judulnya, saya mencoba mencari cara untuk menghasilkan angka acak menggunakan <random>pustaka C ++ 11 yang baru . Saya sudah mencobanya dengan kode ini:

std::default_random_engine generator;
std::uniform_real_distribution<double> uniform_distance(1, 10.001);

Masalah dengan kode yang saya miliki adalah bahwa setiap kali saya mengkompilasi dan menjalankannya, selalu menghasilkan angka yang sama. Jadi pertanyaan saya adalah fungsi lain apa di pustaka acak yang dapat mencapai ini sementara benar-benar acak?

Untuk kasus penggunaan khusus saya, saya mencoba mendapatkan nilai dalam kisaran tersebut [1, 10]


3
Pertanyaan ini berbatasan dengan berbahaya pada "terutama berdasarkan opini." Jika Anda bisa menyingkirkan ajakan meminta pendapat, saya bisa melihat pertanyaan ini sangat berguna (jika belum pernah ditanyakan).
John Dibling

4
Saya sarankan menggunakan a std::mt19937sebagai mesin kecuali Anda punya alasan kuat untuk tidak melakukannya. Dan distribusi adalah interval tertutup di kedua ujungnya.
chris


2
@chris distribusi tidak ditutup di kedua ujungnya, periksa tautan ini atau tautan
memo1288

1
@ memo1288, Terima kasih, saya pikir OP itu menggunakan std::uniform_int_distribution, yang adalah ditutup pada kedua ujungnya.
chris

Jawaban:


202

Stephan T. Lavavej (stl) dari Microsoft berbicara di Going Native tentang cara menggunakan fungsi acak C ++ 11 yang baru dan mengapa tidak menggunakannya rand(). Di dalamnya, ia menyertakan slide yang pada dasarnya menjawab pertanyaan Anda. Saya telah menyalin kode dari slide di bawah.

Anda dapat melihat pembicaraan lengkapnya di sini: http://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

#include <random>
#include <iostream>

int main() {
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_real_distribution<double> dist(1.0, 10.0);

    for (int i=0; i<16; ++i)
        std::cout << dist(mt) << "\n";
}

Kami menggunakan random_devicesekali untuk menyemai generator nomor acak bernama mt. random_device()lebih lambat dari mt19937, tetapi tidak perlu di-seed karena meminta data acak dari sistem operasi Anda (yang akan bersumber dari berbagai lokasi, seperti RdRand misalnya).


Melihat pertanyaan / jawaban ini , tampaknya uniform_real_distributionmengembalikan angka dalam rentang yang [a, b)Anda inginkan [a, b]. Untuk melakukan itu, kita uniform_real_distibutionseharusnya terlihat seperti:

std::uniform_real_distribution<double> dist(1, std::nextafter(10, DBL_MAX));

3
Karena pertanyaannya menanyakan cara paling umum untuk menghasilkan bilangan acak yang mungkin ingin Anda gunakan default_random_engine, menurut primer c ++ itu adalah salah satu yang dianggap paling berguna dalam penerapannya
aaronman

3
@aaronman: Saya mengikuti ceramah STL, di mana dia secara eksplisit tidak suka itu default_random_engineada.
Bill Lynch

5
@chris kita semua tahu perbedaan vector dan map, tidak semua orang tahu perbedaan mt19937 dan ranlux24, jika seseorang berhasil menjadi programmer tanpa mengetahui apa itu vektor dan kamus mungkin mereka harus memiliki a std::default_container, semoga tidak ada orang menganggap diri mereka programmer yang tidak tahu perbedaannya, banyak bahasa skrip memiliki struktur tipe peta default, yang dapat diimplementasikan dalam berbagai cara yang mungkin tidak diketahui pengguna
aaronman

21
The nextafterpanggilan berlebihan untuk sebagian besar aplikasi. Kemungkinan doublependaratan acak tepat di titik akhir sangat kecil sehingga tidak ada perbedaan praktis antara memasukkan dan mengecualikannya.
Mark Ransom

4
@ Chris tidak berhubungan (tapi Anda membuka pintu), Anda std::vectoranalogi tidak bekerja di sini karena std::vector ini benar-benar default yang baik karena CPU caching. Bahkan mengungguli std::listuntuk penyisipan di tengah. Itu benar bahkan jika Anda memahami semua container dan dapat membuat keputusan berdasarkan kerumitan algoritmik.
void.pointer

25

Perpustakaan 'acak' saya menyediakan pembungkus nyaman yang tinggi di sekitar kelas acak C ++ 11. Anda dapat melakukan hampir semua hal dengan metode 'dapatkan' sederhana.

Contoh:

  1. Nomor acak dalam suatu rentang

    auto val = Random::get(-10, 10); // Integer
    auto val = Random::get(10.f, -10.f); // Float point
    
  2. Boolean acak

    auto val = Random::get<bool>( ) // 50% to generate true
    auto val = Random::get<bool>( 0.7 ) // 70% to generate true
    
  3. Nilai acak dari std :: initilizer_list

    auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
    
  4. Iterator acak dari rentang iterator atau semua container

    auto it = Random::get( vec.begin(), vec.end() ); // it = random iterator
    auto it = Random::get( vec ); // return random iterator
    

Dan bahkan lebih banyak hal! Lihat halaman github:

https://github.com/effolkronium/random


7

Saya merah semua hal di atas, sekitar 40 halaman lain dengan c ++ di dalamnya seperti ini dan menonton video dari Stephan T. Lavavej "STL" dan masih tidak yakin bagaimana angka acak bekerja dalam praksis jadi saya mengambil satu hari Minggu penuh untuk mencari tahu tentang apa itu semua dan bagaimana cara kerjanya dan dapat digunakan.

Menurut saya STL benar tentang "tidak menggunakan srand lagi" dan dia menjelaskannya dengan baik di video 2 . Dia juga merekomendasikan untuk menggunakan:

a) void random_device_uniform()- untuk generasi terenkripsi tetapi lebih lambat (dari contoh saya)

b) contoh dengan mt19937- lebih cepat, kemampuan membuat benih, tidak terenkripsi


Saya mengeluarkan semua buku yang diklaim c ++ 11 yang dapat saya akses dan menemukan bahwa Penulis Jerman seperti Breymann (2015) masih menggunakan tiruan dari

srand( time( 0 ) );
srand( static_cast<unsigned int>(time(nullptr))); or
srand( static_cast<unsigned int>(time(NULL))); or

hanya dengan <random>alih - alih <time> and <cstdlib>#includings - jadi berhati-hatilah untuk belajar hanya dari satu buku :).

Artinya - itu tidak boleh digunakan sejak c ++ 11 karena:

Program seringkali membutuhkan sumber bilangan acak. Sebelum standar baru, baik C dan C ++ mengandalkan fungsi pustaka C sederhana bernama rand. Fungsi tersebut menghasilkan bilangan bulat pseudorandom yang didistribusikan secara seragam dalam rentang dari 0 hingga nilai maksimum yang bergantung pada sistem setidaknya 32767. Fungsi rand memiliki beberapa masalah: Banyak, jika tidak sebagian besar, program membutuhkan bilangan acak dalam rentang yang berbeda dari satu diproduksi oleh Rand. Beberapa aplikasi memerlukan angka floating-point acak. Beberapa program membutuhkan angka yang mencerminkan distribusi yang tidak seragam. Pemrogram sering kali memperkenalkan nonrandomness ketika mereka mencoba mengubah rentang, jenis, atau distribusi bilangan yang dihasilkan oleh rand. (kutipan dari Lippmans C ++ primer edisi kelima 2012)


Saya akhirnya menemukan penjelasan terbaik dari 20 buku di Bjarne Stroustrups yang lebih baru - dan dia seharusnya tahu barang-barangnya - di "A tour of C ++ 2019", "Programming Principles and Practice Using C ++ 2016" dan "The C ++ Programming Language 4th edition 2014 "dan juga beberapa contoh di" Lippmans C ++ primer 5th edition 2012 ":

Dan itu sangat sederhana karena generator bilangan acak terdiri dari dua bagian: (1) mesin yang menghasilkan urutan nilai acak atau pseudo-random. (2) distribusi yang memetakan nilai-nilai tersebut ke dalam distribusi matematika dalam suatu rentang.


Terlepas dari pendapat pria STL Microsoft, Bjarne Stroustrups menulis:

Di, perpustakaan standar menyediakan mesin dan distribusi angka acak (§24.7). Secara default gunakan default_random_engine, yang dipilih untuk penerapan luas dan biaya rendah.

The void die_roll()Contoh adalah dari Bjarne Stroustrups - ide yang baik mesin pembangkit dan distribusi dengan using (lebih pertarungan itu di sini) .


Untuk dapat memanfaatkan secara praktis generator bilangan acak yang disediakan oleh pustaka standar di <random> sini beberapa kode yang dapat dieksekusi dengan contoh yang berbeda direduksi menjadi yang paling tidak diperlukan yang semoga aman waktu dan uang untuk kalian:

    #include <random>     //random engine, random distribution
    #include <iostream>   //cout
    #include <functional> //to use bind

    using namespace std;


    void space() //for visibility reasons if you execute the stuff
    {
       cout << "\n" << endl;
       for (int i = 0; i < 20; ++i)
       cout << "###";
       cout << "\n" << endl;
    }

    void uniform_default()
    {
    // uniformly distributed from 0 to 6 inclusive
        uniform_int_distribution<size_t> u (0, 6);
        default_random_engine e;  // generates unsigned random integers

    for (size_t i = 0; i < 10; ++i)
        // u uses e as a source of numbers
        // each call returns a uniformly distributed value in the specified range
        cout << u(e) << " ";
    }

    void random_device_uniform()
    {
         space();
         cout << "random device & uniform_int_distribution" << endl;

         random_device engn;
         uniform_int_distribution<size_t> dist(1, 6);

         for (int i=0; i<10; ++i)
         cout << dist(engn) << ' ';
    }

    void die_roll()
    {
        space();
        cout << "default_random_engine and Uniform_int_distribution" << endl;

    using my_engine = default_random_engine;
    using my_distribution = uniform_int_distribution<size_t>;

        my_engine rd {};
        my_distribution one_to_six {1, 6};

        auto die = bind(one_to_six,rd); // the default engine    for (int i = 0; i<10; ++i)

        for (int i = 0; i <10; ++i)
        cout << die() << ' ';

    }


    void uniform_default_int()
    {
       space();
       cout << "uniform default int" << endl;

       default_random_engine engn;
       uniform_int_distribution<size_t> dist(1, 6);

        for (int i = 0; i<10; ++i)
        cout << dist(engn) << ' ';
    }

    void mersenne_twister_engine_seed()
    {
        space();
        cout << "mersenne twister engine with seed 1234" << endl;

        //mt19937 dist (1234);  //for 32 bit systems
        mt19937_64 dist (1234); //for 64 bit systems

        for (int i = 0; i<10; ++i)
        cout << dist() << ' ';
    }


    void random_seed_mt19937_2()
    {
        space();
        cout << "mersenne twister split up in two with seed 1234" << endl;

        mt19937 dist(1234);
        mt19937 engn(dist);

        for (int i = 0; i < 10; ++i)
        cout << dist() << ' ';

        cout << endl;

        for (int j = 0; j < 10; ++j)
        cout << engn() << ' ';
    }



    int main()
    {
            uniform_default(); 
            random_device_uniform();
            die_roll();
            random_device_uniform();
            mersenne_twister_engine_seed();
            random_seed_mt19937_2();
        return 0;
    }

Saya pikir itu menambahkan semuanya dan seperti yang saya katakan, saya butuh banyak membaca dan waktu untuk menjelaskannya pada contoh-contoh itu - jika Anda memiliki hal-hal lebih lanjut tentang pembuatan bilangan, saya senang mendengarnya melalui pm atau di bagian komentar dan akan menambahkannya jika perlu atau edit posting ini. Bool


0

Inilah sesuatu yang baru saja saya tulis di sepanjang baris itu:

#include <random>
#include <chrono>
#include <thread>

using namespace std;

//==============================================================
// RANDOM BACKOFF TIME
//==============================================================
class backoff_time_t {
  public:
    random_device                      rd;
    mt19937                            mt;
    uniform_real_distribution<double>  dist;

    backoff_time_t() : rd{}, mt{rd()}, dist{0.5, 1.5} {}

    double rand() {
      return dist(mt);
    }
};

thread_local backoff_time_t backoff_time;


int main(int argc, char** argv) {
   double x1 = backoff_time.rand();
   double x2 = backoff_time.rand();
   double x3 = backoff_time.rand();
   double x4 = backoff_time.rand();
   return 0;
}

~



-3

Anda punya dua situasi umum. Yang pertama adalah Anda menginginkan nomor acak dan tidak terlalu mempermasalahkan kualitas atau kecepatan eksekusi. Dalam kasus tersebut, gunakan makro berikut ini

#define uniform() (rand()/(RAND_MAX + 1.0))

yang memberi Anda p dalam kisaran 0 hingga 1 - epsilon (kecuali RAND_MAX lebih besar dari ketepatan ganda, tetapi khawatir tentang itu ketika Anda datang ke sana).

int x = (int) (seragam () * N);

Sekarang berikan bilangan bulat acak pada 0 sampai N -1.

Jika Anda membutuhkan distribusi lain, Anda harus mengubah p. Atau terkadang lebih mudah untuk memanggil uniform () beberapa kali.

Jika Anda ingin perilaku berulang, seed dengan konstanta, jika tidak seed dengan panggilan ke waktu ().

Sekarang jika Anda khawatir tentang kualitas atau kinerja waktu proses, tulis ulang uniform (). Namun sebaliknya jangan sentuh kode tersebut. Selalu pertahankan uniform () pada 0 hingga 1 dikurangi epsilon. Sekarang Anda bisa membungkus pustaka nomor acak C ++ untuk membuat uniform () yang lebih baik, tapi itu semacam opsi tingkat menengah. Jika Anda merasa terganggu dengan karakteristik RNG, ada baiknya juga menginvestasikan sedikit waktu untuk memahami cara kerja metode yang mendasarinya, lalu sediakan. Jadi, Anda memiliki kendali penuh atas kode, dan Anda dapat menjamin bahwa dengan seed yang sama, urutannya akan selalu persis sama, terlepas dari platform atau versi C ++ yang Anda tautkan.


3
Kecuali yang tidak seragam (0 hingga N-1). Alasannya mudah, misalkan N = 100 dan RAND_MAX = 32758. Tidak ada cara untuk memetakan 32758 elemen (RAND_MAX) secara seragam ke 100 input. Cara unik menetapkan batas pada 32000 dan menjalankan ulang rand () jika keluar dari batas
amchacon

1
Jika N adalah 100 maka RNG Anda harus sangat baik untuk dapat mendeteksi penyimpangan dari distribusi datar.
Malcolm McLean
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.