Vì vậy, tôi đã xem một cuộc nói chuyện có tên là rand () Được coi là có hại và nó ủng hộ việc sử dụng mô hình phân phối động cơ của việc tạo số ngẫu nhiên trên std::rand()
mô hình mô đun cộng đơn giản .
Tuy nhiên, tôi muốn std::rand()
tận mắt chứng kiến những thất bại nên tôi đã thực hiện một thử nghiệm nhanh:
- Về cơ bản, tôi đã viết 2 hàm
getRandNum_Old()
vàgetRandNum_New()
điều đó tạo ra một số ngẫu nhiên từ 0 đến 5 bằng cách sử dụngstd::rand()
vàstd::mt19937
+std::uniform_int_distribution
tương ứng. - Sau đó, tôi tạo ra 960.000 (chia hết cho 6) số ngẫu nhiên theo cách "cũ" và ghi lại tần số của các số 0-5. Sau đó, tôi tính toán độ lệch chuẩn của các tần số này. Những gì tôi đang tìm kiếm là độ lệch chuẩn càng thấp càng tốt vì đó là điều sẽ xảy ra nếu phân phối thực sự đồng đều.
- Tôi đã chạy mô phỏng đó 1000 lần và ghi lại độ lệch chuẩn cho mỗi mô phỏng. Tôi cũng ghi lại thời gian tính bằng mili giây.
- Sau đó, tôi làm lại chính xác như vậy nhưng lần này tạo ra các số ngẫu nhiên theo cách "mới".
- Cuối cùng, tôi tính toán giá trị trung bình và độ lệch chuẩn của danh sách độ lệch chuẩn cho cả cách cũ và cách mới, giá trị trung bình và độ lệch chuẩn cho danh sách thời gian được thực hiện cho cả cách cũ và cách mới.
Đây là kết quả:
[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
Điều đáng ngạc nhiên là độ rải tổng hợp của các cuộn là như nhau cho cả hai phương pháp. Tức là std::mt19937
+ std::uniform_int_distribution
không "đồng nhất" hơn std::rand()
+ đơn giản %
. Một quan sát khác mà tôi thực hiện là cách mới chậm hơn khoảng 4 lần so với cách cũ. Nhìn chung, có vẻ như tôi đã phải trả một chi phí lớn về tốc độ mà hầu như không đạt được chất lượng.
Thử nghiệm của tôi có sai sót theo một cách nào đó không? Hay std::rand()
thực sự là không tệ, và thậm chí có thể tốt hơn?
Để tham khảo, đây là mã tôi đã sử dụng toàn bộ:
#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));
}