C++ Chapter 5.3 : 난수 만들기
카테고리: Cpp
태그: Cpp Programming
인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀
🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!
난수를 생성하는 원리
#include <iostream>
using namespace std;
unsigned int PRNG()
{
static unsigned int seed = 5523; // 임의로 선택된 seed값 '5523'
seed = 8235729 * seed + 2396403; // 일부러 overflow를 일으킨다.
return seed % 32768; // [0, 32767] 사이의 값을 반환하게 된다.
}
int main()
{
for (int count = 1; count < 100; ++count) // 난수를 100개 생성한다.
{
cout << PRNG() << "\t"; // 난수 생성 후 출력
if (count % 5 == 0) cout << endl; // 5개출력마다 개행
}
}
static unsigned int seed = 5523
- seed : 난수 생성 시작 숫자
- 5523 : 임의로 선택된 시드 넘버
static
: 난수 생성 원리의 포인트!
seed = 8235729 * seed + 2396403;
- unsigned int 로 담지 못하는 overflow 문제를 만들어낸다.
- 최대한 시드와 관련없고 규칙없고 예측 못하게 난수를 생성하기 위하여.
static
변수는 값이 메모리에 유지가 된다.- 따라서 5523으로의 초기화는 딱 한번만 이루어지고 이 부분에서 seed값이 변하므로 난수를 생성할 때마다 (PRNG 함수가 호출될 때 마다) 변한 seed값으로 연산을 하게 된다.
- unsigned int 로 담지 못하는 overflow 문제를 만들어낸다.
- 이 같은 원리로 인하여 seed 넘버가 똑같으면 생성되는 난수 패턴도 똑같다.
- 그래서 seed 넘버를 상수로 하지 않고 매 순간 변하는 시간을 int 로 형변환해 설정하기도 한다. 밑에 참고
라이브러리를 사용하여 난수 생성하기
<csdlib>
std::rand()
: seed로부터 랜덤 값 생성std::srand(6545)
: seed 넘버를 6545로 설정 ex) std::srand(6545) 로 시드 넘버를 설정해준 후 std::rand() 하여 랜던 값 가져오기- 다만 이렇게 시드 넘버가 상수이면 프로그램 실행할 때마다 생성되는 난수 패턴이 똑같다는게 한계점
<ctime>
std::time()
: 1970년 1월 1일을 기준으로 현재까지 경과된 시간을 초 단위로 반환한다.
std::srand(statoc_cast<unsigned int>(std::time(0))); // 시드 넘버 설정
for (int count = 1; count <=100; ++count)
{
cout << std::rand() << "\t"; // 난수 생성 및 출력
if (count % 5 == 0) count << endl;
}
- 시드 넘버를
statoc_cast<unsigned int>(std::time(0))
로 설정- 현재 시간(초)를 unsigned int로 형변환한 것을 시드 넘버로 설정
- 시드 넘버를 시간으로 설정해주면 프로그램 실행시마다 난수 패턴을 다르게 할 수 있다.
- 다만 디버깅 할 땐 시드넘버를 상수로 하는 것을 추천한다.
특정한 정수 범위 사이의 난수 생성하기
#include <iostream>
#include <cstdlib> //std::rand(), std::srand()
#include <ctime> //std:: time()
using namespace std;
int getRandomNumber(int min, int max)
{
static const double fraction = 1.0 / (RAND_MAX + 1.0); // 역수
return min + static_cast<int>((max - min + 1)* (std::rand()*fraction));
}
int main()
{
std::srand(static_cast<unsigned int>(std::time(0))); // 시드 값은 시간으로.
for(int count = 1; count <=100; ++count)
{cout << getRandomNumber(5, 8)<< "\t"; // 탭 맞춰주기
if (count % 5 == 0) cout << endl; // 5개 출력할 때마다 개행
return 0;
}
int getRandomNumber(int min, int max)
static
- 나누기 연산자체가 보통 느려서 초기화때만 나누기 연산 한번만 실행될 수 있도록 static으로 정의.
- fraction 값은 보존될 것
RAND_MAX
- 난수를 생성해서 나올 수 있는 가장 큰 숫자.
- 매크로로 정의가 되어있다.
fraction
- RAND_MAX + 1 의 역수
return min + static_cast<int>((max - min + 1)* (std::rand()*fraction));
- 난수를 생성한후 fraction을 곱해주어 [0, 1) 범위의 난수로 만든다.
- 이를 또
(max - min + 1)
를 곱해주어 [0, max-min+1) 범위의 난수로 만든다. - 이를 int로 형 변환하여 [0, max-min+) 범위의 정수인 난수로 만든다.
- min을 더해주면 최종적으로 [min, max + 1) 범위 내에 있는 난수가 리턴될 것.
getRandomNumber(5, 8)
: 5 6 7 8 중 하나가 리턴된다.
for(int count = 1; count <=100; ++count)
{
cout << std::rand() %4 + 5 << "\t";
if (count % 5 == 0) cout << endl; /
}
rand() % 4 + 5
- rand() % 4 -> 0 , 1 , 2 , 3 값 중 하나가 될텐데
- 여기에 5를 더해주면 5, 6, 7, 8 값 중 하나가 된다.
- 이 방법의 문제점은 특정 범위로 몰릴 수가 있다는 것이다. 정밀한 경우에는 쓰지 않는 것이 좋다.
<random> 라이브러리
- C++ 11 부터 사용 가능하다.
- 난수가 특정 범위로 몰려 생성될 경우를 없애준다.
- 각 숫자가 나올 확률을 전부 동일하게 해줌
using namespace std;
int main()
{
std::random_device rd; // 시드 넘버 생성. time처럼 시드 넘버 계속 바뀌게 한다.
std::mt19937_64 mersenne(rd()); // 랜덤 넘버 생성 알고리즘. mersenne는 알고리즘 이름이다.
std::uniform_int_distribution<> dice(1, 6); // 1~6 사이의 수를 랜덤하게 만든다. 단, 각 숫자가 나올 확률은 전부 동일하다.
for (int count = 1; count <= 20; ++count)
cout << dice(mersenne) << endl; // 균등하게 랜덤 넘버 생성
return 0;
}
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기