2024.02.22 - 난수 생성
난수: 무작위로 선택된 수. 사실 컴퓨터공학에서 완벽한 난수라는 건 없다고 한다.
난수를 생성하는 방법에는 여러가지가 있다.
- rand()
- srand()
rand(), srand()를 사용하기 위해서는 <cstdlib>을 include해야하지만, <iostream>을 include하는 것으로도 쓸 수 있다.
rand()를 사용하면 0 ~ 32767 까지의 랜덤한 수를 얻을 수 있다.
그런데 코드를 다시 실행해보면 이상한 점을 느낄 수 있다.
아무리 실행해봐도 같은 값이 생성된다는 것이다. 이래서야 랜덤한 값이라고 할 수 없다.
rand()가 같은 값을 생성하는 이유는 컴퓨터가 특정한 시작 값으로 난수를 생성하기 때문이다.
이 시작 값을 시드 값이라고 한다. 시드 값을 이용해 생성된 난수 값은 다음 난수를 구하기 위한 시드 값이 된다.
프로그램을 실행시킬때 마다 같은 시드 값으로 수를 생성했으니 같은 난수가 생성될 수 밖에 없다.
프로그램마다 다른 난수를 생성시키고 싶다면, 시드값을 변경해주어야한다.
srand()를 사용하면 괄호 안의 값을 기반으로 시드 값을 변경시킬 수 있다.
일단, 41로 시작하는 난수 세트에서 벗어났다.
그런데 srand()에 매번 달라지는 값을 넣지 못하면, 프로그램을 실행할 때마다 생성되는 시드 값이 같아질 것이다.
srand()에 프로그램을 실행하는 순간마다 다른 값을 넣기 위해서 보통 time() 함수를 사용한다.
- time(NULL)
time()은 1970년 01월 01일 00시 00분 00초 로부터 현재시간 까지 흐른 시간을 초단위로 나타내어 정수로 반환해준다.
time()을 사용하기 위해서는 <ctime>을 include 해야한다고 하지만, 어째선지 <iostream>만 include해도 사용할 수 있었다. time()의 매개변수로는 NULL을 사용하는 것 같다.
실행할 때마다 점점 증가한 수가 나온다. 이것을 이용하면 srand의 매개변수로 실행 때마다 다른 값을 넣을 수 있을 것 같다.
매번 실행할 때마다 다른 난수가 나온다.
그러나 프로그램을 연속해서 실행시켜보면 생성되는 난수가 비슷비슷해 보이는 것을 확인할 수 있다.
이는 시드 값이 시간을 기반으로 하다보니 실행할 때마다 비슷한 값이여서 생성된 난수도 크게 다르지 않게 되는 것이다.
더 질 좋은 난수를 얻기 위해서는 다른 방법이 필요하다.
- <random>라이브러리를 사용한 난수 생성
이건 일단 예시 코드를 보면서 이해를 해보자.
실행결과
한줄 한줄 천천히 알아보자.
#include<random>
우선, 난수를 생성하기 위해 <random>라이브러리를 사용하고 싶기 때문에 <random>을 include 해주었다.
std::random_device rd;
앞에서는 시드 값을 얻기 위해 time()을 사용했지만 더 양질의 시드 값을 얻기 위해서 random_device를 사용한다. random_device가 제공하는 난수는 컴퓨터가 주변환경과 무작위적으로 상호작용하면서 만들어진 것이기 때문에 진짜 난수라고 볼 수 있다.
와 그럼 그냥 저거 사용해서 난수 생성하면 되겠다. 이렇게~
계속 돌려봐도 아주 랜덤하게 잘 생성한다.
그런데 문제가 있다. random_device를 사용해서 난수를 만드는 과정은 매우 느리다. 많은 난수를 생성할 때는 이 값을 계속 뽑아내기 보다는 random_device로 얻은 난수 하나를 시드값으로 사용해서 난수 생성 엔진을 돌리는 것이 낫다.
std::mt19937 mt(rd());
std::mt19937 는 C++ <random> 라이브러리에서 제공하는 난수 생성 엔진 중 하나로, 메르센 트위스터 라는 알고리즘을 사용한다. 이 알고리즘은 rand()보다 더 질 좋은 난수를 생성하게 해준다고 한다.
위 코드는 random_device에서 생성한 난수를 시드 값으로 넣은 mt라는 이름의 난수 생성 엔진을 만드는 코드이다.
std::uniform_int_distribution<int> dis(0, 10);
난수 생성 엔진을 만들었다고 바로 난수를 생성할 수는 없다. 어떤 범위에서 난수를 생성할 지 분포를 정의해야 한다.
여러가지 분포가 있지만, 범위 안에서 균등한 확률로 난수를 생성하고 싶다면 균등분포(Uniform distrubution) 객체를 정의해야 한다. 위 코드는 0부터 10까지의 정수를 뽑기 위한 균등분포를 dis라는 이름으로 정의하는 코드이다.
int mtRandomNumber = dis(mt);
마지막으로 균등분포에 사용할 난수엔진을 전달함으로서 난수를 생성할 수 있다.