본문으로 바로가기

Random 난수 발생에 대한 검토

category 개발언어/C# 2017. 10. 1. 21:32

컴퓨터 과학 분야에서 말하는 난수는 보통 결정론적인 방법으로 생성된 난수이다.
특정 입력이나 조건에 따라 무작위로 선택된 것처럼 보이는 난수또는 난수열이 생성되며 그 생성 조건이나 입력이 같다면 그 결과값은 항상 같다.
진정한 의미에서의 난수는 아니지만 그 결과값이 충분히 추측되기 어렵다면 어느정도 난수로서의 의미를 가질 수 있다.

컴퓨터에 의해 생성된 모든 난수는 의사(擬似) 난수이다.
컴퓨터는 계산된 결과만 가지고 난수를 생성할 수 있는데 이 계산된 결과는 입력값에 의해 결정된 값이기 때문에 이 값을 가지고는 난수를 생성할 수 없다.
컴퓨터의 최초 시동시 난수표가 생성되어 컴퓨터내에 보관된다.
하지만 컴퓨터 프로그래밍에서 매번 같은 방법으로 이 값을 가지고 오려고 한다면 매번 그 값은 같기 때문에 최초 한번 호출할 때를 제외하고는 난수라고 볼 수 없다.
그래서 보통 씨앗 값(Seed)이라 불리는 수를 인자로 매번 다르게 주어 매번 다른 의사난수를 추출하여 사용한다. 이 씨앗 값은 보통 시간을 이용한다.
여기서 시간은 보통 현재 시간을 의미한다. 매 순간 현재시간이 바뀌며 한 번 지나간 시간은 다시 돌아오지 않는다는 특성은 이전에 발생했던 의사난수 또는 의사난수열을 재연 불가능하게 만들며, 이 시간이 밀리초 단위로 섬세하게 표현된다면 사람에의한 임의적 조작도 사실상 불가능해진다.


C++의 Rand
rand는 난수를 만드는 함수이며, stdlib.h 파일을 포함시켜야 한다.
rand() 는 0부터 stdlib.h 파일에서 지정되있는 0x7fff까지의 범위를 가진 난수(임의의수)를 출력한다.
0x7fff는 32767이니까 0~32767의 범위 사이에서 난수가 생성된다는 말이다.
임의의 값을 추출한다는 의미가 같은 값이 중복되지 않는 다는 의미는 아니다.

실제로 Rand()를 반복해서 실행시켜 보면 값이 순서대로 나열되지는 않지만 계속 동일한 결과가 반복되는 것을 볼 수 있을 것이다.
앞에서 설 명했듯이 최초한번만 난수가 생성되었다고 하더라도, 다음에도 같은 결과가 반복된다면 난수는 의미가 없다.
그래서 Seed값을 주어서 매번 다른 값을 출력하게 한다. 즉 srand(time(NULL)); 와 같은 코드를 추가하여 매번 다른 값을 출력 하게 한다.

1
2
3
4
5
6
7
8
9
10
11
12
#include
#include
#include 
int main()
{
int i;
srand(time(NULL));
for(i=0; i<10; i++)
printf("%d ", rand()%10);
printf("\n")
return 0;
}

C++ Random  범위지정 :대부분의 프로그램에서 우리는 0~32767의 난수를 모두 사용하지 않으므로 Random함수의 출력 범위를 설정하여 사용한다.
그러기 위해 “%” 연산자를 사용한다.   나눗셈을 하여 몫만 취하게 한다는 것이다.
위의 소스 코드는 10으로 나누었기 때문에 결과는 0~9 사이의 값만이 존재한다.
rand()%10+1 과 같이 코딩하면 1~10 사이의 값이 항상 출력 될 것이다.


C# Random
C#의 경우 srand와 같이 Seed를 사용할 필요가 없다고 하는데 글쎄 바람직 하지는 않은것 같다.
C# 과 JAVA는 rand함수가 따로 있는 것이 아니라 Class로 구현되어 있어서
아래예와 같이 사용 할 수 있다, 또한 난수 발생범위를 지정 할 수 있다.

1
2
3
4
5
6
7
int i;
Random r = new Random(); 
//RandomRandom rand = new Random(DateTime.Now.Millisecond);
for (i = 0; i < 10; i++)
{
   Console.WriteLine("{0} = {1}", i + 1, r.Next(1, 100));//1~100 까기의 난수 발생
}

난수 발생시 중복 방지하기
Random을 사용하여 생성되는 값을 범위를 임의로 제한 했으므로 범위안에서는 중복된 값이 있을 수 있다.
그러므로 중복된 값에 대한 처리가 필요하다.
로또 번호 생성과 같은 경우  6개의 배열을 만들고 같은 번호가 중복되면 무시하고 한번 더 난수를 발생시켜 저장하는 방식으로 중복없는 값을 추출할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include
#include
#include
  
int main()
{
       int rn=0;
       int a[6];
       int i, j;   
       srand((unsigned int)time(NULL));
       for ( i = 0; i < 6; ++i )
       {
           rn= rand() % 45+ 1;                       
           for ( j = 0; j < i; ++j )
           {
              if ( a[j] == rn )// 같은값이 이미있으므로
              {
                  --i;   //인덱스를 증가 하지 않고 For(i = 문을 한번 더 실행해서 새로윤 난수 생생시킴           
                  break;
              }         
           }
           a[i]=rn ;
       }
}