728x90
728x90
SMALL
난수를 생성하는 원리
#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값으로 연산을 하게 된다. - 이 같은 원리로 인하여 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;
}
728x90
300x250
LIST
'C │ C++ │ C# > C++' 카테고리의 다른 글
C++ Chapter 6.1 : 배열 기초 (0) | 2023.08.26 |
---|---|
C++ Chapter 5.4 : std::cin 더 잘쓰기 (0) | 2023.08.25 |
C++ Chapter 5.2 : 반복문$($while, do-while, for$)$과 점프$($break, continue, goto$)$ (0) | 2023.08.22 |
C++ Chapter 5.1 : 조건 분기 (if문, switch-case문) (0) | 2023.08.22 |
C++ Chapter 4.8 : 구조체 (0) | 2023.08.21 |