TEUS.me

 
 

반응형

프로그램을 하나 만들면서 최초 실행부터 난수를 생성하도록 할 일이 생겼다.

당연히도 수많은 강좌에서 사용하라고 강요(?)하는 방식으로 시작했다.

 

srand((unsigned)(time(NULL)));
int r = rand();

 

물론 프로그램은 정상적으로(?) 동작했고, 특별한 문제는 없었다.

단지 생성된 난수가 너무 규칙적이란 부분 하나만 빼면...

 

C/C++언어에 포함된 rand() 함수가 그다지 품질도 좋지 않고, 속도도 빠르지 않다는 얘긴 많았다.

MT(메르센 트위스터)WELL을 사용하란 글도 쉽게 찾을 수 있다.

그래도 개인 용도로 사용할 때는 간단하고 편하게 쓸 수 있는 rand()를 사용해왔다.

 

낌새가 이상해서 VS2022에서 프로그램 하나를 간단히 만들어 돌려봤다.

 

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <windows.h>
#include <synchapi.h>

int main()
{
    for (int i = 0; i < 60; ++i) {
        unsigned u = (unsigned)(time(NULL));
        srand(u);

        int n = rand();
        int n1 = rand();

        printf("%u, %d, %d\n", u, n, n1);

        Sleep(1000);
    }
}

 

매 루프마다 srand()를 실행[각주:1]한 뒤 rand()를 두 번 실행해서 난수를 두 개 생성해봤다.

결과는 아래와 같았다.

 

1652925156, 16518, 15222
1652925157, 16521, 25971
1652925159, 16528, 14700
1652925160, 16531, 25448
1652925161, 16534, 3428
1652925162, 16537, 14177
1652925163, 16541, 24925
1652925164, 16544, 2906
1652925165, 16547, 13654
1652925166, 16550, 24402
1652925167, 16554, 2383
1652925168, 16557, 13131
1652925169, 16560, 23880
1652925170, 16563, 1860
1652925171, 16567, 12609
1652925172, 16570, 23357
1652925173, 16573, 1337
1652925174, 16577, 12086
1652925175, 16580, 22834
1652925176, 16583, 815
1652925177, 16586, 11563
1652925178, 16590, 22311
1652925179, 16593, 292
1652925180, 16596, 11040
1652925181, 16599, 21789
1652925182, 16603, 32537
1652925183, 16606, 10517
1652925184, 16609, 21266
1652925185, 16612, 32014
1652925186, 16616, 9995
1652925187, 16619, 20743
1652925188, 16622, 31491
1652925189, 16626, 9472
1652925190, 16629, 20220
1652925191, 16632, 30969
1652925192, 16635, 8949
1652925193, 16639, 19697
1652925194, 16642, 30446
1652925195, 16645, 8426
1652925196, 16648, 19175
1652925197, 16652, 29923
1652925198, 16655, 7903
1652925199, 16658, 18652
1652925200, 16661, 29400
1652925201, 16665, 7381
1652925202, 16668, 18129
1652925203, 16671, 28878
1652925204, 16675, 6858
1652925205, 16678, 17606
1652925206, 16681, 28355
1652925207, 16684, 6335
1652925208, 16688, 17084
1652925209, 16691, 27832
1652925210, 16694, 5812
1652925211, 16697, 16561
1652925212, 16701, 27309
1652925213, 16704, 5290
1652925214, 16707, 16038
1652925215, 16710, 26786
1652925216, 16714, 4767

 

이게 바로 그 뭔가 쎄한 느낌의 증거였다.

srand() 직후 수행한 rand()의 결과가 균일하지 않은 정도를 넘어 아예 서서히 증가하는 것이다.

그래프로 그려보면 아래와 같다.

 

이게 무려 rand() 함수의 리턴값이라고!!!

 

그나마 첫번째 값은 쓰레기라고 치고, 두번째 값을 그냥 쓰는 건 어떨까 해서 둘을 겹쳐봤다.

 

네... 이게 놀랍게도 rand() 함수의 리턴값 맞습니다

 

아... 쓰레기네....

비슷한 테스트를 몇번 한 적이 있었는데, 그 때도 이렇게 쓰레기였나... 싶은 생각이 들 정도로 그냥 쓰레기다.

 

혹시나 다른 컴파일러는 어떤가 해서 OnlineBGD에서 GCC(9.3.0)로 돌려봤다.

 

 

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <unistd.h>

int main()
{
    for (int i = 0; i < 60; ++i) {
        unsigned u = (unsigned)(time(NULL));
        srand(u);

        int n = rand();
        int n1 = rand();

        printf("%u, %d, %d\n", u, n, n1);

        sleep(1);
        
    }
}

 

코드 자체는 VS2022 코드와 거의 차이가 없다.

Sleep() 관련 부분만 살짝 차이가 있다.

 

결과는 아래와 같다.

 

1652925174, 1232824342, 1183772582
1652925175, 1996616898, 2063085534
1652925176, 1695350575, 1877563542
1652925177, 1380508352, 610808271
1652925178, 4707165, 420252323
1652925179, 782841118, 1320222958
1652925180, 1550592171, 57895992
1652925181, 1241101882, 944281875
1652925182, 2007591155, 749286576
1652925183, 1712340952, 573956943
1652925184, 333125912, 381305764
1652925185, 1096334848, 1264838010
1652925186, 789773927, 1074100166
1652925187, 1583020078, 914155254
1652925188, 194972576, 1789527298
1652925189, 955692015, 1593793021
1652925190, 1732316270, 1417306091
1652925191, 349331278, 146035000
1652925192, 55609151, 2118521532
1652925193, 821704874, 854999073
1652925194, 504079181, 653985131
1652925195, 1285578498, 481053359
1652925196, 2052830169, 293742085
1652925197, 678011451, 1176245649
1652925198, 1448122554, 2070948854
1652925199, 62541144, 800510266
1652925200, 1904319664, 1686922712
1652925201, 530214411, 1497267030
1652925202, 1290245681, 235046111
1652925203, 996269831, 56785902
1652925204, 693158671, 2016528017
1652925205, 393362014, 1838923907
1652925206, 1152712852, 1636232406
1652925207, 853717352, 382070495
1652925208, 551952024, 199416075
1652925209, 253641228, 18832682
1652925210, 1024733042, 907141496
1652925211, 1795829974, 724752743
1652925212, 408772619, 1601228675
1652925213, 111726779, 346330597
1652925214, 1940313090, 148289206
1652925215, 566030290, 2107925070
1652925216, 263089839, 847785546
1652925217, 1041071934, 1746137362
1652925218, 732232859, 1558924925
1652925219, 424800691, 1365509576
1652925220, 127865401, 110765641
1652925221, 894382238, 995371034
1652925222, 587433393, 807166781
1652925223, 283477982, 1691362530
1652925224, 2134034595, 440197637
1652925225, 751040704, 1319030132
1652925226, 443042748, 1131635024
1652925227, 1214509153, 2022554000
1652925228, 905253207, 1825467258
1652925229, 1691273462, 582949913
1652925230, 300420434, 384524598
1652925231, 1065689198, 193340170
1652925232, 1835015074, 1078836744
1652925233, 469544357, 908483221

 

첫번째 값을 그래프로 찍어보면 아래와 같다.

 

 

VS2022보다는 훨씬 더 난수라는 느낌을 보여주지만, 이 역시 뭔가 패턴이 보인다.

고개를 오른쪽으로 45도 꺾어서 보면 뭔가 쎄하다.

 

 

첫번째 값과 두번째 값을 함께 보면 그래도 좀 난수 같긴 하다.

두번째 값은 첫번째 값보단 패턴 같은 게 덜 보이는 것 같기도 하고.

 


 

참고로, WELL512로 생성해본 난수는 아래와 같다[각주:2].

 

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <windows.h>
#include <synchapi.h>
#include "MyWELL512.h"

int main()
{
    for (int i = 0; i < 60; ++i) {
        unsigned u = (unsigned)(time(NULL));
        MyWELL512::InitWELLRNG512withTime();

        int n = (int)(MyWELL512::WELLRNG512l() % RAND_MAX);
        int n1 = (int)(MyWELL512::WELLRNG512l() % RAND_MAX);

        printf("%u, %d, %d\n", u, n, n1);

        Sleep(1000);
    }
}

 

결과는 아래와 같다.

 

1652926159, 14757, 9846
1652926160, 2351, 31533
1652926161, 22505, 4436
1652926162, 21246, 5831
1652926163, 22203, 5506
1652926164, 31329, 30655
1652926165, 7526, 30036
1652926166, 1403, 29271
1652926167, 28397, 13536
1652926168, 2514, 18442
1652926169, 15526, 21966
1652926170, 25571, 4236
1652926171, 21776, 5001
1652926172, 28226, 10009
1652926173, 32274, 11870
1652926174, 31494, 9670
1652926175, 20584, 2296
1652926176, 16034, 31206
1652926177, 3305, 22635
1652926178, 19065, 4663
1652926179, 23040, 25154
1652926180, 1451, 25
1652926182, 874, 27314
1652926183, 24569, 3548
1652926184, 26995, 8139
1652926185, 26128, 130
1652926186, 13468, 13134
1652926187, 23444, 22185
1652926188, 12965, 20928
1652926189, 20398, 10421
1652926190, 17226, 5582
1652926191, 8542, 2660
1652926192, 4626, 8809
1652926193, 1491, 32156
1652926194, 4367, 2701
1652926195, 6145, 7766
1652926196, 20344, 23198
1652926197, 6052, 19175
1652926198, 6594, 19777
1652926199, 9875, 6620
1652926200, 297, 30277
1652926201, 29496, 18635
1652926202, 25191, 1519
1652926203, 1396, 32700
1652926204, 14188, 23562
1652926205, 55, 15739
1652926206, 22107, 4626
1652926207, 19512, 18530
1652926208, 929, 1367
1652926209, 19618, 19705
1652926210, 32251, 18556
1652926211, 12766, 22846
1652926212, 24651, 19768
1652926213, 1033, 7175
1652926214, 31185, 28537
1652926215, 17825, 15494
1652926216, 14034, 16671
1652926217, 25498, 11437
1652926218, 5611, 22144
1652926219, 21704, 12169

 

매번 시간을 기준으로 초기화를 수행한 뒤에 생성된 첫번째 난수값의 그래프이다.

 

 

그냥 난수다.

아무리 봐도 패턴 같은 건 없는 난수다.

 

두번째로 생성한 값과 함께 보면 아래와 같다.

 

 

역시 난수다.

 

결론: rand() 버리고 WELL512 같은 거 쓰자, 심지어 걔들이 더 빠름

 

 

  1. 물론, 실제로는 이렇게 하면 안 되고, 최초 실행시에 한번만 srand()를 수행하는 게 정석임 [본문으로]
  2. WELL512 쪽 코드는 생략, 본진 가면 쉽게 얻을 수 있음 [본문으로]
반응형

'IT > 알고리즘' 카테고리의 다른 글

CMap vs std::map  (4) 2022.09.13
Visual C++에서 Epoch time 계산하기  (0) 2022.08.28
memcpy() 계열 최적화?  (0) 2022.02.14
조건에 맞는 기약분수의 갯수  (0) 2021.08.22
최대공약수(GCD)를 구하는 가장 빠른 방법은?  (0) 2021.08.22

공유하기

facebook twitter kakaoTalk kakaostory naver band

댓글

비밀글모드

  1. 과거에는 그런 이유로 .. 만들어 쓰곤 했지요 .. 그리고 srand 의 잦은 호출은 오히려 ;; 더 심한 패턴화를 불러오더라구요..
    값 자체는 다르더라도 난수가 생성되는 패턴은 거의 비슷하게 발생합니다 윈도의 rand 는 ..
    근데 어짜피 컴퓨터의 랜덤은 랜덤할수 없는 놈이니까요 .. 애초에 ..
    2022.05.24 13:31 신고
    • 그래도 저 정도면 너무 심하니까요... ㅎㅎㅎ
      2022.05.26 08:15 신고