Test uygulamanızın en temel sorunu, bir srand
kez aramanız ve sonra rand
bir kez aramanız ve çıkmanızdır.
srand
Fonksiyonun tüm noktası, rasgele bir tohumla sözde rasgele sayılar dizisini başlatmaktır .
Bu geçtiğiniz takdirde demektir aynı değeri için srand
(aynı olan iki farklı uygulamalarda srand
/ rand
uygulaması) daha sonra tam olarak aynı diziyi alacak ait rand()
değerleri her iki uygulamada da bundan sonra okuyun.
Bununla birlikte, örnek uygulamanızda sözde rasgele sıra yalnızca tek bir öğeden oluşur - tohumdan üretilen sözde rasgele sıralamanın ilk öğesi, geçerli second
hassasiyet zamanına eşittir . Peki çıktıda ne görmeyi bekliyorsunuz?
Açıkçası uygulamayı aynı saniyede çalıştırdığınızda - aynı tohum değerini kullanıyorsunuz - sonuçlarınız elbette aynıdır (Martin York'un soruya bir yorumda daha önce bahsettiği gibi).
Aslında srand(seed)
bir kez aramalı ve sonra rand()
birçok kez aramalı ve bu diziyi analiz etmelisiniz - rastgele görünmelidir.
DÜZENLE:
Ah anladım. Görünüşe göre sözlü açıklama yeterli değil (belki dil engeli veya başka bir şey ... :)).
TAMAM. Soruda srand()/rand()/time()
kullanılan işlevlerin aynısını temel alan eski moda C kodu örneği :
#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;
}
^ ^ ^ Programın tek bir işleminden bu dizinin rastgele görünmesi bekleniyor.
EDIT2:
C veya C ++ standart kütüphanesini kullanırken, şu andan itibaren gerçekten rastgele veri üreten (standart tarafından garanti edilen) tek bir standart fonksiyon veya sınıfın olmadığını anlamak önemlidir. Bu soruna yaklaşan tek standart araç, maalesef hala gerçek rastgelelik garantisi vermeyen std :: random_device'dir .
Uygulamanın doğasına bağlı olarak, gerçekten gerçekten rasgele (öngörülemeyen) verilere ihtiyacınız olup olmadığına karar vermelisiniz. Kesinlikle gerçek rastgelelik istediğinizde dikkate değer bir durum bilgi güvenliğidir - örneğin simetrik anahtarlar, asimetrik özel anahtarlar, tuz değerleri, güvenlik belirteçleri vb.
Ancak güvenlik sınıfı rasgele sayılar, ayrı bir makaleye değer ayrı bir endüstridir.
Çoğu durumda Sözde Rastgele Sayı Üreteci yeterlidir - örneğin bilimsel simülasyonlar veya oyunlar için. Bazı durumlarda tutarlı bir şekilde tanımlanmış sözde rastgele sıra bile gereklidir - örneğin oyunlarda çok fazla veri depolamaktan kaçınmak için çalışma zamanında tam olarak aynı haritaları oluşturmayı seçebilirsiniz.
Orijinal soru ve aynı / benzer soruların tekrarlayan çok sayıda (ve hatta onlara yanlış yönlendirilmiş birçok "cevap"), ilk önce ve en önemlisi rastgele sayıları sahte rastgele sayılardan ayırmanın ve sahte rastgele sayı dizisinin ne olduğunu anlamanın önemli olduğunu gösterir. ilk sırada VE sözde rasgele sayı üreteçlerinin gerçek rastgele sayı üreteçlerini kullanabileceğiniz şekilde KULLANILMADIĞINI fark etmek.
Sezgisel olarak rastgele sayı talep ettiğinizde - döndürülen sonuç önceden döndürülen değerlere bağlı olmamalı ve daha önce herhangi bir şey talep edip etmediğine bağlı olmamalı ve hangi anda ve hangi süreçte ve hangi bilgisayarda ve hangi jeneratörden ve hangi jeneratörden ve hangi galaksi istendi. Sonuçta "rastgele" kelimesi ne anlama geliyor - tahmin edilemez ve herhangi bir şeyden bağımsız olmak - aksi halde artık rastgele değil, değil mi? Bu sezgiyle, herhangi bir olası bağlamda bu kadar rasgele bir sayı elde etmek için web'de bazı sihirli büyüleri aramak doğaldır.
Bu tür sezgisel beklentiler , gerçek rastgele sayılar için makul olmasına rağmen , Sahte Rastgele Sayı Üreteçleri içeren tüm durumlarda ÇOK YANLIŞ ve zararlıdır .
"Rastgele sayı" nın anlamlı kavramı mevcut olsa da, "sözde rastgele sayı" diye bir şey yoktur. Bir Sahte Rastgele Sayı Üreteci aslında sahte rastgele sayı dizisi üretir .
Uzmanlar PRNG'nin kalitesi hakkında konuştuğunda, oluşturulan sekansın (ve onun önemli alt sekanslarının) istatistiksel özellikleri hakkında konuşurlar. Örneğin, iki yüksek kaliteli PRNG'yi her ikisini de sırayla kullanarak birleştirirseniz - her biri ayrı ayrı iyi sekanslar üretmelerine rağmen kötü sonuçlanan sekans üretebilirsiniz (bu iki iyi sekans birbiriyle kolayca ilişkili olabilir ve böylece kötü bir şekilde birleşebilir).
Yalancı rasgele dizi aslında her zaman belirleyicidir (algoritması ve başlangıç parametreleri tarafından önceden belirlenir) yani aslında rastgele bir şey yoktur.
Spesifik olarak rand()
/ srand(s)
fonksiyon çifti, uygulama tanımlı algoritmayla üretilen, işlem başına tekil, iş parçacığı için güvenli olmayan (!) Sözde rastgele bir sayı dizisi sağlar. İşlev rand()
aralıkta değerler üretir [0, RAND_MAX]
.
C11 standardından alıntı:
srand
İşlev sonraki çağrıları tarafından döndürülecek yalancı rasgele sayılar yeni dizisi için bir tohum olarak bağımsız değişken kullanır rand
. Daha
srand
sonra aynı tohum değeri ile çağrılırsa, sözde rasgele sayılar dizisi tekrarlanmalıdır. Herhangi rand
bir çağrı srand
yapılmadan srand
önce çağrılırsa, ilk olarak 1 tohum değeri ile çağrıldığı zamanki gibi aynı sıra oluşturulmalıdır .
Birçok kişi makul bekliyoruz rand()
aralığında yarı bağımsız düzgün yayılı bir sayı dizisi üretecektir 0
için RAND_MAX
. Eh kesinlikle (aksi takdirde işe yaramaz) gerekir ama maalesef sadece standart gerektirmez - "üretilen rastgele dizinin kalitesi konusunda hiçbir garanti yoktur" diye açık bir feragatname vardır . Bazı tarihsel durumlarda rand
/ srand
uygulama gerçekten çok kötü nitelikteydi. Modern uygulamalarda olsa bile, muhtemelen yeterince iyidir - ancak güven kırılır ve kurtarılması kolay değildir. İş parçacığı için güvenli olmayan doğasının yanı sıra, çok iş parçacıklı uygulamalarda güvenli kullanımını zor ve sınırlı hale getirir (yine de mümkündür - bunları sadece tek bir iş parçacığından kullanabilirsiniz).
Yeni sınıf şablonu std :: mersenne_twister_engine <> (ve kolaylık typedefs - std::mt19937
/ std::mt19937_64
iyi şablon parametreleri kombinasyonu ile) C ++ 11 standardında tanımlanan nesne başına sahte rastgele sayı üreteci sağlar. Aynı şablon parametreleri ve aynı başlatma parametreleriyle, farklı nesneler, C ++ 11 uyumlu standart kitaplıkla oluşturulmuş herhangi bir uygulamadaki herhangi bir bilgisayarda tam olarak aynı nesne başına çıktı dizisini üretecektir. Bu sınıfın avantajı, öngörülebilir yüksek kaliteli çıktı dizisi ve uygulamalar arasında tam tutarlılığıdır.
Ayrıca C ++ 11 standardında tanımlanan daha fazla PRNG motoru vardır - std :: linear_congruential_engine <> (tarihsel olarak srand/rand
bazı C standart kütüphane uygulamalarında adil kalite algoritması olarak kullanılır ) ve std :: subtract_with_carry_engine <> . Ayrıca, tamamen tanımlanmış parametreye bağlı nesne başına çıkış dizileri oluştururlar.
Yukarıdaki eski C kodu için modern C ++ 11 örnek değişimi:
#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;
}
Önceki kodun std :: uniform_int_distribution <> kullanan sürümü
#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;
}