<random> Linux'ta aynı sayıyı üretir, ancak Windows'ta oluşturmaz


91

Aşağıdaki kod, [1,100] aralığında beş sözde rastgele sayının bir listesini oluşturmayı amaçlamaktadır. Ben tohuma default_random_engineile time(0)de sistem saatini döndüren Unix zaman . Bu programı Microsoft Visual Studio 2013 kullanarak Windows 7 üzerinde derleyip çalıştırdığımda beklendiği gibi çalışıyor (aşağıya bakın). Ancak bunu g ++ derleyicisiyle Arch Linux'ta yaptığımda, garip davranıyor.

Linux'ta her seferinde 5 numara üretilecektir. Son 4 numara her yürütmede farklı olacaktır (genellikle olduğu gibi), ancak ilk numara aynı kalacaktır.

Windows ve Linux'ta 5 yürütmeden örnek çıktı:

      | Windows:       | Linux:        
---------------------------------------
Run 1 | 54,01,91,73,68 | 25,38,40,42,21
Run 2 | 46,24,16,93,82 | 25,78,66,80,81
Run 3 | 86,36,33,63,05 | 25,17,93,17,40
Run 4 | 75,79,66,23,84 | 25,70,95,01,54
Run 5 | 64,36,32,44,85 | 25,09,22,38,13

Gizeme ek olarak, bu ilk sayı Linux'ta periyodik olarak bir artar. Yukarıdaki çıktıları aldıktan sonra yaklaşık 30 dakika bekledim ve 1. sayının değiştiğini ve şimdi hep 26 olarak üretildiğini bulmaya çalıştım. Periyodik olarak 1 artmaya devam etti ve şimdi 32'de. Karşılık geliyor gibi görünüyor. değişen değeri ile time(0).

Neden ilk sayı çalıştırmalar arasında nadiren değişiyor ve sonra değiştiğinde 1 artıyor?

Kod. 5 sayıyı ve sistem saatini düzgün bir şekilde yazdırır:

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

using namespace std;

int main()
{
    const int upper_bound = 100;
    const int lower_bound = 1;

    time_t system_time = time(0);    

    default_random_engine e(system_time);
    uniform_int_distribution<int> u(lower_bound, upper_bound);

    cout << '#' << '\t' << "system time" << endl
         << "-------------------" << endl;

    for (int counter = 1; counter <= 5; counter++)
    {
        int secret = u(e);
        cout << secret << '\t' << system_time << endl;
    }   

    system("pause");
    return 0;
}

3
Nedir sizeof(time_t)vs sizeof(default_random_engine::result_type)?
Mark Ransom

3
default_random_engineBu iki platformda tamamen farklı olduğunu unutmayın .
TC

1
Hala rastgele BTW olabilir.
Alec Teal

5
Her programcı, zamanın iyi bir rastgele sayı üreteci tohumu olduğunu düşündüğü bir aşamadan geçiyor mu?
OldFart

6
@OldFart Evet, buna akademi deniyor.
Casey

Yanıtlar:


141

İşte neler oluyor:

  • default_random_enginelibstdc ++ 'da (GCC'nin standart kitaplığı), minstd_rand0basit bir doğrusal uyumlu motordur:

    typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_rand0;
    
  • Bu motorun rastgele sayılar üretme şekli x i + 1 = (16807x i + 0) mod 2147483647'dir.

  • Bu nedenle, tohumlar 1'den farklıysa, o zaman çoğu zaman üretilen ilk sayı 16807 farklı olacaktır.

  • Bu jeneratörün aralığı [1, 2147483646] 'dır. Libstdc ++ 'nın uniform_int_distributiononu [1, 100] aralığındaki bir tamsayıya eşleme şekli esasen şudur: bir sayı üret n. Sayı 2147483600'den büyük değilse geri dönün (n - 1) / 21474836 + 1; aksi takdirde yeni bir numara ile tekrar deneyin.

    Vakaların büyük çoğunluğunda nyalnızca 16807 farklı olan iki s'nin bu prosedür altında [1, 100] 'de aynı sayıyı vereceğini görmek kolay olmalıdır . Aslında, üretilen sayının her 21474836/16807 = 1278 saniye veya 21,3 dakikada bir artması beklenir ki bu, gözlemlerinizle oldukça uyumludur.

MSVC en default_random_engineolduğu mt19937bu sorunu yok ki.


36
GCC'nin standart kitaplığının geliştiricilerinin böylesine korkunç bir varsayılanı seçmesine neyin sebep olduğunu merak ediyorum.
CodesInChaos

13
@CodesInChaos ile ilişkili olup olmadığını bilmiyorum ama MacOS / iOS araç zinciri aynı korkunç rastgele motoru kullanıyor, rand()% 7 her zaman 0
döndürüyor

7
@ LưuVĩnhPhúc Tamir etmemek rand()biraz anlaşılabilir bir şey (bu umutsuz miras saçmalığı). Yeni bir şey için çok katmanlı bir PRNG kullanmak affedilemez. Standart, "nispeten sıradan, uzman olmayan ve / veya hafif kullanım için en azından kabul edilebilir motor davranışı sağlamayı" gerektirdiğinden, bunu standart bir ihlal olarak bile düşünebilirim. ki bu uygulama, rand % 7örneğiniz gibi önemsiz kullanım durumlarında bile feci şekilde başarısız olduğu için sağlamaz .
CodesInChaos

2
@CodesInChaos Neden rand()tam olarak düzeltmek biraz anlaşılabilir değil ? Bu sadece kimsenin yapmayı düşünmemiş olabileceği için mi?
user253751

2
@immibis API o kadar bozuk ki, tüm sorunları düzelten bağımsız bir yedek ile daha iyi durumda olursunuz. 1) Algoritmayı değiştirmek büyük bir değişiklik olacaktır, bu nedenle muhtemelen eski programlar için bir uyumluluk anahtarına ihtiyacınız olacaktır. 2) Tohumu, srandkolayca benzersiz tohumlar oluşturmak için çok küçük. 3) Arayanın istenen aralıktaki bir sayıya bir şekilde indirgemek zorunda olduğu, uygulama tanımlı bir üst sınıra sahip bir tamsayı döndürür; bu, doğru şekilde yapıldığında, rand()4 için mantıklı bir API ile bir değiştirme yazmaktan daha fazla iştir ) Global değişken durumu kullanır
CodesInChaos

30

std::default_random_engineUygulama tanımlanır. Kullanın std::mt19937veya std::mt19937_64bunun yerine.

Ek olarak std::timeve ctimeişlevler çok doğru değil, <chrono>bunun yerine başlıkta tanımlanan türleri kullanın:

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

int main()
{
    const int upper_bound = 100;
    const int lower_bound = 1;

    auto t = std::chrono::high_resolution_clock::now().time_since_epoch().count();

    std::mt19937 e;
    e.seed(static_cast<unsigned int>(t)); //Seed engine with timed value.
    std::uniform_int_distribution<int> u(lower_bound, upper_bound);

    std::cout << '#' << '\t' << "system time" << std::endl
    << "-------------------" << std::endl;

    for (int counter = 1; counter <= 5; counter++)
    {
        int secret = u(e);

        std::cout << secret << '\t' << t << std::endl;
    }   

    system("pause");
    return 0;
}

3
Bir sözde rastgele değişken oluşturucuyu tohumlarken daha doğru bir zaman kullanmak istenir mi? Belki bu saflıktır, ancak entropiyi ortaya çıkarırsa, yanlışlık neredeyse arzu edilir gibi geliyor. (Daha az kesin olduğunu ve dolayısıyla maddi olarak daha az potansiyel tohumla sonuçlandığını söylemediğiniz sürece.)
Nat

15
std::random_deviceRastgele oluşturucunuzu tohumlamak için current_time yerine kullanmanızı öneririm . Lütfen Random ile ilgili herhangi bir cppreference örneğini kontrol edin.
Aleksander Fular

5
Kimsenin tohumunuzu tahmin etmesini (ve dolayısıyla dizinizi yeniden üretmesini) istemiyorsanız, daha az kesinlik daha fazla rastgelelikle aynı şey değildir. En uç noktaya gidelim: Tohumunuzu ertesi güne (veya yıla?) Yuvarlayın -> tahmin etmek kolaydır. Femtosaniye hassasiyetini kullanın -> Yapacak çok fazla tahmin ...
linac

2
@ChemicalEngineer öğesinin ayrıntı ctimedüzeyi 1 saniyedir. Uygulamaların ayrıntı std::chronodüzeyi kullanıcı tanımlıdır, varsayılan olarak std::high_resolution_clock(Visual Studio'da bu bir typedef for std::steady_clock), nanosaniye olarak belirlenir, ancak çok daha küçük bir ölçüm seçebilir, dolayısıyla çok daha kesin.
Casey

2
@linac Kriptografik özellikler istiyorsanız, uygun prng kullanırsınız (bu cevapta kullanılmayan). Ve elbette, vaat edilen hassasiyet ne olursa olsun, zamana dayalı tohum da söz konusu değildir.
Cthulhu

-2

Linux'ta rastgele işlev, yolun olasılıksal anlamında rastgele bir işlev değil, sözde bir rasgele sayı üretecidir. Bir tohumla tuzlanır ve bu tohuma göre, üretilen sayılar sözde rasgele ve üniform olarak dağıtılır. Linux yöntemi, popülasyonlardan gelen bilgileri kullanan belirli deneylerin tasarımında, deneyin tekrarının, girdi bilgilerinde bilinen ince ayarlarla ölçülebilmesi avantajına sahiptir. Nihai program gerçek hayat testi için hazır olduğunda, tuz (tohum), kullanıcıdan fareyi hareket ettirmesi, fare hareketini bazı tuş vuruşlarıyla karıştırması ve başlangıçtan bu yana bir miktar mikrosaniye sayımları eklemesi istenerek oluşturulabilir. son güç.

Windows rasgele sayı tohumu, fare, klavye, ağ ve günün saati sayılarının toplanmasından elde edilir. Tekrarlanamaz. Ancak bu tuz değeri, yukarıda belirtildiği gibi, bir deneyin tasarımına dahil edilirse, bilinen bir tohuma sıfırlanabilir.

Oh evet, Linux iki rastgele sayı üretecine sahiptir. Biri, varsayılan modulo 32bits ve diğeri modulo 64bit'tir. Seçiminiz, doğruluk ihtiyaçlarına ve testiniz veya fiili kullanımınız için tüketmek istediğiniz işlem süresi miktarına bağlıdır.


5
Tohum üretme algoritmasından neden bahsettiğinden emin değilim. OP, açıkça sistem zamanını tohum olarak kullanır. Ayrıca, bazı referanslar ekleyebilirsinizcollection of mouse, keyboard, network and time of day numbers
yerel varsayılan ayar
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.