Bu yüzden rand () Zararlı Olarak Kabul Edildi adlı bir konuşma gördüm ve basit std::rand()
artı modül paradigması üzerinden rastgele sayı üretiminin motor dağıtım paradigmasını kullanmayı savundu .
Ancak, başarısızlıkları std::rand()
ilk elden görmek istedim, bu yüzden hızlı bir deney yaptım:
- Temel olarak, 2 işlev yazdım
getRandNum_Old()
vegetRandNum_New()
bu, sırasıylastd::rand()
vestd::mt19937
+ kullanarak 0 ile 5 arasında rastgele bir sayıstd::uniform_int_distribution
ürettim. - Sonra "eski" yolu kullanarak 960.000 (6'ya bölünebilir) rastgele sayı ürettim ve 0-5 sayılarının frekanslarını kaydettim. Sonra bu frekansların standart sapmasını hesapladım. Aradığım şey, olabildiğince düşük bir standart sapma, çünkü dağılım gerçekten tekdüze olsaydı böyle olurdu.
- Bu simülasyonu 1000 kez çalıştırdım ve her simülasyon için standart sapmayı kaydettim. Ayrıca milisaniye cinsinden geçen zamanı da kaydettim.
- Daha sonra, aynısını tekrar yaptım ama bu sefer rastgele sayılar üretmek "yeni" yol.
- Son olarak, hem eski hem de yeni yol için standart sapmalar listesinin ortalamasını ve standart sapmasını ve hem eski hem de yeni yol için alınan zamanların listesi için ortalama ve standart sapmayı hesapladım.
Sonuçlar şunlardı:
[OLD WAY]
Spread
mean: 346.554406
std dev: 110.318361
Time Taken (ms)
mean: 6.662910
std dev: 0.366301
[NEW WAY]
Spread
mean: 350.346792
std dev: 110.449190
Time Taken (ms)
mean: 28.053907
std dev: 0.654964
Şaşırtıcı bir şekilde, merdanelerin toplam yayılması her iki yöntem için de aynıydı. Yani, std::mt19937
+ std::uniform_int_distribution
basit std::rand()
+ 'dan "daha tek tip" değildi %
. Yaptığım bir başka gözlem de yeninin eski yoldan yaklaşık 4 kat daha yavaş olduğuydu. Genel olarak, neredeyse hiç kalite kazanımı olmadan hız için çok büyük bir maliyet ödüyormuşum gibi görünüyordu.
Deneyim bir şekilde kusurlu mu? Yoksa std::rand()
gerçekten o kadar da kötü değil ve belki daha da iyi mi?
Referans için, işte bütünüyle kullandığım kod:
#include <cstdio>
#include <random>
#include <algorithm>
#include <chrono>
int getRandNum_Old() {
static bool init = false;
if (!init) {
std::srand(time(nullptr)); // Seed std::rand
init = true;
}
return std::rand() % 6;
}
int getRandNum_New() {
static bool init = false;
static std::random_device rd;
static std::mt19937 eng;
static std::uniform_int_distribution<int> dist(0,5);
if (!init) {
eng.seed(rd()); // Seed random engine
init = true;
}
return dist(eng);
}
template <typename T>
double mean(T* data, int n) {
double m = 0;
std::for_each(data, data+n, [&](T x){ m += x; });
m /= n;
return m;
}
template <typename T>
double stdDev(T* data, int n) {
double m = mean(data, n);
double sd = 0.0;
std::for_each(data, data+n, [&](T x){ sd += ((x-m) * (x-m)); });
sd /= n;
sd = sqrt(sd);
return sd;
}
int main() {
const int N = 960000; // Number of trials
const int M = 1000; // Number of simulations
const int D = 6; // Num sides on die
/* Do the things the "old" way (blech) */
int freqList_Old[D];
double stdDevList_Old[M];
double timeTakenList_Old[M];
for (int j = 0; j < M; j++) {
auto start = std::chrono::high_resolution_clock::now();
std::fill_n(freqList_Old, D, 0);
for (int i = 0; i < N; i++) {
int roll = getRandNum_Old();
freqList_Old[roll] += 1;
}
stdDevList_Old[j] = stdDev(freqList_Old, D);
auto end = std::chrono::high_resolution_clock::now();
auto dur = std::chrono::duration_cast<std::chrono::microseconds>(end-start);
double timeTaken = dur.count() / 1000.0;
timeTakenList_Old[j] = timeTaken;
}
/* Do the things the cool new way! */
int freqList_New[D];
double stdDevList_New[M];
double timeTakenList_New[M];
for (int j = 0; j < M; j++) {
auto start = std::chrono::high_resolution_clock::now();
std::fill_n(freqList_New, D, 0);
for (int i = 0; i < N; i++) {
int roll = getRandNum_New();
freqList_New[roll] += 1;
}
stdDevList_New[j] = stdDev(freqList_New, D);
auto end = std::chrono::high_resolution_clock::now();
auto dur = std::chrono::duration_cast<std::chrono::microseconds>(end-start);
double timeTaken = dur.count() / 1000.0;
timeTakenList_New[j] = timeTaken;
}
/* Display Results */
printf("[OLD WAY]\n");
printf("Spread\n");
printf(" mean: %.6f\n", mean(stdDevList_Old, M));
printf(" std dev: %.6f\n", stdDev(stdDevList_Old, M));
printf("Time Taken (ms)\n");
printf(" mean: %.6f\n", mean(timeTakenList_Old, M));
printf(" std dev: %.6f\n", stdDev(timeTakenList_Old, M));
printf("\n");
printf("[NEW WAY]\n");
printf("Spread\n");
printf(" mean: %.6f\n", mean(stdDevList_New, M));
printf(" std dev: %.6f\n", stdDev(stdDevList_New, M));
printf("Time Taken (ms)\n");
printf(" mean: %.6f\n", mean(timeTakenList_New, M));
printf(" std dev: %.6f\n", stdDev(timeTakenList_New, M));
}