그래서 저는 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() +% . 내가 만든 또 다른 관찰은 새로운 것이 이전 방식보다 약 4 배 느리다는 것입니다. 전반적으로 품질 향상은 거의없이 속도면에서 엄청난 비용을 지불하는 것 같았습니다.
내 실험에 어떤 식 으로든 결함이 있습니까? 아니면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));
}