[C++] 진짜 랜덤값? 가짜 랜덤값?

닿메_dahme
|2025. 1. 14. 21:49

저번 포스팅에서 올렸던 좀비 클래스를 구현하다가 랜덤함수를 검색했는데 진짜 랜덤값 이라는 키워드가 궁금증을 자극해서 알아보게 되었다.

 

먼저 랜덤값하면 말 그대로 어떤 값이 나올지 모르는, 출력이 되어야만 알 수 있는 그런 숫자라고 생각한다.

사실 게임 속에서도 아이템을 획득하거나 뽑기를 할 때 랜덤으로 확률을 정하고 결과를 얻는 부분이 많이 있다.

그래서 구현할 때는 모두 랜덤함수를 사용했을거라 생각했던 나...

 

하지만 랜덤값에도 가짜가 있다는 사!실! 알고있었나요...?

 

먼저 rand 함수와 srand()는 무엇인지 궁금해졌다.

Seed 숫자(=시드)
  • 컴퓨터가 난수를 생성할 때 사용하는 특정한 시작 숫자.
  • 이 숫자를 기준으로 정해진 알고리즘을 실행하면서 마치 난수처럼 보이는 수열을 생성하는 것

 

srand()
  • rand 함수에서 사용될 수를 초기화하는 역할
  • 매개변수로 seed 값을 받아서 초기화
  • 정해진 값을 넣게되면 rand 값도 항상 정해지기 때문에 랜덤값이 아님
    • 즉 항상 변하는 값을 넣어줘야하는데 사람들이 생각해낸 것이 바로 시간값

 

rand() 함수 특징
  • 랜덤하게 생성되는 숫자의 크기가 최대 32767까지
  • 생성되는 숫자의 분포가 균일하지 않다
  • 알고리즘이 있기 때문에 설정한 Seed 숫자만 알면 어떤 숫자가 랜덤으로 생성되는지 알 수 있다
  • 프로그램이 생성될 때 값이 정해지기 때문에 여러 번 실행시켜도 동일한 값이 나온다
  • 시간(time)으로 시드 생성(C 스타일)

 

랜덤값 테스트

과연 가짜 랜덤값이 무엇인지 확인해보기 위해 랜덤으로 숫자를 추출하는 간단한 코드로 테스트를 진행해보았다.

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;
int main()
{
    time_t now = time(0);
    tm localTime;

    localtime_s(&localTime, &now);

    cout << "현재 시간: ";
    cout << localTime.tm_hour << ":"
        << localTime.tm_min << ":"
        << localTime.tm_sec << endl;

    for (int i = 0; i < 10; ++i) {
        int num = rand() % 10;
        cout << num << endl;
    }

    return 0;
}

 

결과는??

 

1, 7, 4, 0, 9, 4, 8, 8, 2, 4가 나왔다.

 

자 그럼 다시 한 번 실행해보면 랜덤으로 값이 나오는거니까 또 다른 10개의 값이 출력되겠지?

으잉? 이게 무슨 일이야 같은 값이 나온다.

이유는 처음 실행할 때 난수를 지정하고 그게 저장이 되어서 다음 실행을 해도 똑같은 값이 나오기 때문이다.

 

이걸 해결하기 위해 rand로 값을 반환하기 전에 strand에 시드값으로 항상 다른 값이 나오는 시간을 넣어준다.

기존 작성한 코드에 srand(time(nullptr)); 한 줄을 추가해준다.

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;
int main()
{
    time_t now = time(0);
    tm localTime;

    localtime_s(&localTime, &now);

    cout << "현재 시간: ";
    cout << localTime.tm_hour << ":"
        << localTime.tm_min << ":"
        << localTime.tm_sec << endl;

    srand(time(nullptr));
    for (int i = 0; i < 10; ++i) {
        int num = rand() % 10;
        cout << num << endl;
    }

    return 0;
}

 

결과는?

 

 

이렇게 시간값을 시드로 넣어주니 항상 값이 변하는 것을 볼 수 있다.

하지만 rand 함수는 정해진 알고리즘이 실행되면서 값을 반환하기 때문에 시드값이 어떤 것이 들어갔는지 알아낼 수 있다면 어떤 랜덤값이 나오는지 알 수 있다.

 

그럼 과연 진짜 랜덤값은 무엇일까?

 

<random> 라이브러리
  • 더 좋은 시드값을 위해 random_device 제공
  • std::random_device는 균일한 분포(uniformly-distributed)를 가진 비결정적(non-deterministic)인 int 숫자를 생성하는 클래스
  • 다양한 난수 생성 엔진과 분포(distribution)를 제공

 

 

std::random_device는 균일한 분포(uniformly-distributed)를 가진 비결정적(non-deterministic)인 int 숫자를 생성하는 클래스이다.

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <random>

using namespace std;
int main()
{
    time_t now = time(0);
    tm localTime;

    localtime_s(&localTime, &now);

    cout << "현재 시간: ";
    cout << localTime.tm_hour << ":"
        << localTime.tm_min << ":"
        << localTime.tm_sec << endl;

    random_device rd;

    cout << "Min Value : " << rd.min() << ", " << "Max Value : " << rd.max() << endl;

    for (int i = 0; i < 3; i++) {
        cout << rd() << endl;
    }

    return 0;
}

 

 

이렇게 하면 random_device가 제공하는 최대 최소값으로 랜덤값에 접근할 수 있다.

하지만 내가 범위를 지정하고 싶다면?

 

바로 분포를 이용하면 된다

#include <iostream>
#include <random>

int main() {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dist(0, 99); // 0~99 범위임의로 지정했따

    for (int i = 0; i < 5; ++i) {
        std::cout << dist(gen) << " ";
    }
    return 0;
}

이렇게 겉으로 보기엔 좀 어려워보이지만 클래스 선언만 하고 분포값만 지정해주면 간단하게 진짜 랜덤한 값을 뽑아낼 수 있는 것이다.

 

정리해보자면 radnom_device로 랜덤한 시드를 받아온 다음, 이 시드를 기반으로 mt19937에서 알고리즘이 동작해서 랜덤값이 나오게 되는 것이다.

 

마무리말

여기까지 랜덤값을 어떻게 가져오면 균등한 확률로 뽑아낼 수 있을지에 대해 알아보았다.

공부하다보니 아직 배울 내용이 너무 많다는 걸 느꼈고 배웠던 내용도 시간이 지나면 잊어버리게 되어 꾸준히 복습하는 것이 중요하다는 것을 느꼈다.

그리고 구현되어있는 내장함수의 구조는 어떤지, 무슨 타입의 값을 받고 리턴하는지 자세히 들여다볼 수 있는 기회가 되었다.

 

'C++' 카테고리의 다른 글

[ C++ ] Text_RPG 팀플 완료  (1) 2025.01.17
[Visual Studio] 한글 깨짐 현상  (0) 2025.01.15
[C++] Text RPG 몬스터 생성 로직 설계  (1) 2025.01.13
[C++] 컴파일러와 인터프리터?  (1) 2025.01.03
[C++] Template, STL  (0) 2025.01.02