C ++ 11 rasgele kitaplığı kullanarak rasgele sayılar üretme


135

Başlıktan da anlaşılacağı gibi, yeni C ++ 11 <random>kitaplığını kullanarak rasgele sayılar üretmenin bir yolunu bulmaya çalışıyorum . Bu kod ile denedim:

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

Ben sahip kod ile sorun her derlemek ve çalıştırmak, her zaman aynı numaraları üretir olmasıdır. Öyleyse sorum şu ki, rastgele kütüphanedeki diğer işlevler gerçekten rastgele iken bunu başarabilir mi?

Özel kullanım durumum için, aralık dahilinde bir değer elde etmeye çalışıyordum [1, 10]


3
Bu soru tehlikeli bir şekilde "öncelikle görüşe dayalı" ile sınırlıdır. Görüş için talepten kurtulabilirseniz, bu sorunun çok yararlı olduğunu görebilirim (daha önce sorulmamışsa).
John Dibling

4
std::mt19937Eğer iyi bir nedeniniz yoksa a'yı motor olarak kullanmanızı öneririm . Ve dağıtım her iki uçta kapalı bir aralıktır.
chris


2
@ dağıtım dağıtım her iki uçta da kapalı değil, bu bağlantıyı veya bu bağlantıyı
memo1288

1
@ memo1288, Teşekkür ederim, OP'nin her iki ucunda da kapalı std::uniform_int_distributionolan bir a kullandığını düşündüm .
chris

Yanıtlar:


191

Microsoft'tan Stephan T. Lavavej (stl), Going Native'da yeni C ++ 11 rastgele işlevlerinin nasıl kullanılacağı ve neden kullanılmayacağı hakkında bir konuşma yaptı rand(). İçinde, temelde sorunuzu çözen bir slayt ekledi. Aşağıdaki slayttan kodu kopyaladım.

Tam konuşmasını burada görebilirsiniz: 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";
}

Bir random_devicekez rasgele sayı üretecini adlandırmak için kullanırız mt. random_device()daha yavaştır mt19937, ancak işletim sisteminizden ( örneğin RdRand gibi çeşitli konumlardan kaynaklanacak) rasgele veriler istediği için tohumlanması gerekmez .


Bu soruya / cevaba baktığımızda uniform_real_distribution, aralıktaki bir sayıyı [a, b)istediğiniz yere döndüren anlaşılıyor [a, b]. Bunu yapmak için, uniform_real_distibutionaslında şöyle görünmeliyiz:

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

3
Soru, sadece kullanmak isteyebileceğiniz rastgele sayılar üretmenin en genel yolunu istediğinden default_random_engine, c ++ primerine göre, uygulamanın en faydalı olduğu
sorundur

2
@aaronman: STL'nin konuşmasına gidiyorum, burada açıkça hoşlanmıyor default_random_engine.
Bill Lynch

5
@chris hepimiz bir vektör ve bir harita arasındaki farkı biliyoruz, herkes mt19937 ve ranlux24 arasındaki farkı bilmiyor, eğer birisi bir vektörün ve sözlüğün ne olduğunu bilmeden bir programcı olmayı başarabilirse, belki de bir olması gerekir std::default_container, umarım hayır insanlar kendilerini farklılıkları bilmeyen programcılar olarak düşünüyorlar, çok sayıda komut dosyası dili, kullanıcının
bilmeyebileceği

21
nextafterÇağrı çoğu uygulama için overkill. doubleTam olarak uç noktaya rastgele iniş şansı o kadar küçüktür ki, dahil etmek ve hariç tutmak arasında pratik bir fark yoktur.
Mark Ransom

3
senin İlgisiz @chris (ama kapıyı açtı) std::vectorçünkü analoji burada çalışmıyor std::vector ise aslında CPU'ya önbelleğe alma nedeniyle iyi bir varsayılan. std::listOrtada yerleştirme için bile daha iyi performans gösterir . Tüm kapları anlasanız ve algoritmik karmaşıklığa dayalı bilinçli bir karar verebilseniz bile bu doğrudur.
void.pointer

24

Benim 'rastgele' kütüphanem C ++ 11 rastgele sınıflar etrafında yüksek uygun bir sarıcı sağlar. Hemen hemen her şeyi basit bir 'get' yöntemiyle yapabilirsiniz.

Örnekler:

  1. Aralıktaki rastgele sayı

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

    auto val = Random::get<bool>( ) // 50% to generate true
    auto val = Random::get<bool>( 0.7 ) // 70% to generate true
  3. Bir std'den rastgele değer :: initilizer_list

    auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
  4. Yineleyici aralığından veya tüm kapsayıcıdan rastgele yineleyici

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

Ve daha fazlası! Github sayfasına göz atın:

https://github.com/effolkronium/random


4

Ben gibi İçinde c 40 diğer sayfalara ++ hakkında, malzeme üzerindeki tüm kırmızı bu izlediler Stephan T. Lavavej "STL" video ve hala anlamaya Tam bir Pazar aldı kadar emin nasıl rasgele sayılar eserler praksisinde değildi ne hakkında ve nasıl çalışır ve kullanılabilir.

Bence STL "artık srand kullanmamak" konusunda haklı ve video 2'de iyi açıkladı . Ayrıca şunları kullanmanızı önerir:

a) void random_device_uniform() - şifreli nesil için ancak daha yavaş (örneğimden)

b) ile örnekler mt19937- daha hızlı, şifrelenmemiş tohum oluşturma yeteneği


Eriştiğim tüm iddia edilen c ++ 11 kitaplarını çıkardım ve Breymann (2015) gibi Alman Yazarların hala bir klon kullandıklarını gördüm

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

sadece #includings <random>yerine <time> and <cstdlib>- bu yüzden sadece bir kitaptan öğrenmeye dikkat edin :).

Anlamı - c ++ 11'den beri kullanılmamalıdır çünkü:

Programlar genellikle rastgele sayılar kaynağına ihtiyaç duyar. Yeni standarttan önce, hem C hem de C ++, rand adlı basit bir C kitaplığı işlevine dayanıyordu. Bu işlev, 0 aralığında, en az 32767 olan sisteme bağlı bir maksimum değere eşit olarak dağıtılan psödondom tamsayıları üretir. Rand işlevinin çeşitli sorunları vardır: Çoğu olmasa da, programların çoğundan farklı bir aralıkta rasgele sayılar gerekir biri rand. Bazı uygulamalar rastgele kayan nokta sayıları gerektirir. Bazı programlar, düzgün olmayan bir dağılımı yansıtan sayılara ihtiyaç duyar. Programcılar genellikle rand tarafından üretilen sayıların aralığını, türünü veya dağılımını dönüştürmeye çalıştıklarında rasgele olmamaya başlarlar. (Lippmans C ++ primer beşinci baskı 2012'den alıntı)


Sonunda Bjarne Stroustrups'un yeni kitaplarındaki 20 kitaptan en iyi açıklamasını buldum - ve onun eşyalarını bilmeli - "C ++ 2019 Turu", "C ++ 2016 Kullanarak Programlama Prensipleri ve Uygulamaları" ve "C ++ Programlama Dili 4. baskı" 2014 "ve" Lippmans C ++ primer beşinci baskı 2012 "deki bazı örnekler:

Ve bu gerçekten basit çünkü rastgele bir sayı üreticisi iki bölümden oluşur: (1) rastgele veya sözde rastgele değerler dizisi üreten bir motor. (2) bu değerleri bir aralıktaki matematiksel dağılıma eşleyen bir dağılım.


Microsofts STL adamının görüşüne rağmen, Bjarne Stroustrups şöyle yazıyor:

İçinde, standart kütüphane rasgele sayı motorları ve dağılımları sağlar (§24.7). Varsayılan olarak, geniş uygulanabilirlik ve düşük maliyet için seçilen default_random_engine kullanın.

void die_roll()Örnek Bjarne Stroustrups dan - iyi bir fikir üreten motor ve dağıtımı ile using (daha o burada dersin) .


Burada standart kütüphane tarafından sağlanan rasgele sayı üreteçlerini pratik olarak kullanabilmek için, bazı örnekler ile bazı yürütülebilir kodlar, umarım sizin için umarım güvenli olan zaman ve paradan en aza <random> indirgenmiştir:

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

Sanırım her şeyi ekliyor ve dediğim gibi, bu örneklere onu okumanız bir sürü okuma ve zaman aldı - eğer sayı üretimi hakkında daha fazla şey varsa, pm veya yorum bölümünde bunu duymaktan mutluluk duyuyorum gerekiyorsa ekleyecek veya bu yayını düzenleyecektir. Bool


0

İşte bu satırlarda yazdığım bir şey ::

#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

İki yaygın durumunuz var. Birincisi, rastgele sayılar istemeniz ve kalite veya yürütme hızı hakkında çok fazla endişelenmemenizdir. Bu durumda, aşağıdaki makroyu kullanın

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

bu, p'yi 0 ila 1 - epsilon aralığında verir (RAND_MAX bir çiftin hassasiyetinden daha büyük değilse, ancak bunun için endişelenmeyin).

int x = (int) (düzgün () * N);

Şimdi 0 ila N -1 arasında rastgele bir tam sayı verir.

Başka dağıtımlara ihtiyacınız varsa, p'yi dönüştürmeniz gerekir. Veya bazen uniform () öğesini birkaç kez aramak daha kolaydır.

Tekrarlanabilir davranış istiyorsanız, sabit bir tohum ekin, aksi takdirde bir zaman çağrısı () ile tohumlayın.

Şimdi kalite veya çalışma süresi performansından rahatsızsanız, uniform () yazınız. Aksi takdirde koda dokunmayın. Her zaman 0 ila 1 eksi epsilon üzerinde eşit () tutun. Şimdi daha iyi bir üniforma () oluşturmak için C ++ rastgele sayı kitaplığını sarabilirsiniz, ancak bu bir tür orta düzey seçenek. RNG'nin özelliklerinden rahatsızsanız, alttaki yöntemlerin nasıl çalıştığını anlamak için biraz zaman ayırmaya değer, sonra bir tane sağlayın. Böylece kod üzerinde tam bir kontrole sahip olursunuz ve aynı tohumla dizinin, platformdan veya hangi C ++ sürümüne bağlandığınıza bakılmaksızın her zaman tam olarak aynı olacağını garanti edebilirsiniz.


3
Bunun dışında tek tip değil (0 ila N-1). Nedeni kolay, diyelim ki N = 100 ve RAND_MAX = 32758. 32758 elemanı (RAND_MAX) 100 girişle aynı şekilde eşleştirmenin bir yolu yok. Eşsiz yol
32000'de bir

1
N 100 ise, düz bir dağılımdan sapmayı tespit edebilmek için RNG'niz son derece iyi olmalıdır.
Malcolm McLean
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.