Тож я побачив виступ під назвою rand (), який вважається шкідливим, і він виступав за використання парадигми розподілу двигуна генерації випадкових чисел над простою std::rand()
парадигмою плюс модуль.
Однак я хотів побачити недоліки з std::rand()
перших вуст, тому провів швидкий експеримент:
- В принципі, я написав 2 функції
getRandNum_Old()
і ,getRandNum_New()
що генерується випадкове число в діапазоні від 0 до 5 включно , використовуючиstd::rand()
іstd::mt19937
+std::uniform_int_distribution
відповідно. - Потім я сформував 960 000 (ділених на 6) випадкових чисел, використовуючи "старий" спосіб, і записав частоти чисел 0-5. Потім я розрахував середньоквадратичне відхилення цих частот. Що я шукаю, це стандартне відхилення якнайнижче, оскільки саме це могло б статися, якби розподіл був справді рівномірним.
- Я провів це моделювання 1000 разів і записав стандартне відхилення для кожного моделювання. Я також записав час, який знадобився в мілісекундах.
- Згодом я знову зробив те саме, але цього разу генерував випадкові числа "новим" способом.
- Нарешті, я розрахував середнє та стандартне відхилення списку стандартних відхилень як для старого, так і нового шляху, а також середнє та стандартне відхилення для переліку часу, прийнятого як для старого, так і для нового способу.
Ось результати:
[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
На диво, сукупний розподіл рулонів був однаковим для обох методів. Тобто, std::mt19937
+ std::uniform_int_distribution
не був "більш рівномірним", ніж простий std::rand()
+ %
. Іншим зауваженням, яке я зробив, було те, що нове було приблизно в чотири рази повільніше, ніж колишнє. Загалом, здавалося, що я плачу величезні витрати на швидкість майже за відсутність виграшу в якості.
Мій експеримент якимось чином помилковий? Або std::rand()
насправді не все так погано, а може, навіть краще?
Для довідки, ось код, який я використав повністю:
#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));
}