IDL/Math

중복되지 않는 난수(random number) 산출법

이상우_IDL 2015. 7. 10. 13:18
728x90

IDL에서 난수(Random Number)를 만들기 위해서 RANDOMU나 RANDOMN과 같은 내장함수를 사용하면 된다는 것은 이미 많이 알려져 있는 사실입니다. 그런데, 한꺼번에 여러 개의 난수들을 생성했을 때, 그 숫자들 중 서로 값이 똑같은 경우가 발생할 수 있을까요? 예를 들어 다음과 같이 6개의 난수를 한꺼번에 만들어서 출력해보면, 그게 매우 어려운 일이라는 것을 짐작할 수 있습니다.


IDL> PRINT, RANDOMU(seed, 6)

     0.847488     0.329893     0.788283     0.929697     0.512887     0.244529


물론 이런 명령을 여러번 반복적으로 수행해서 출력된 결과를 봐도 거의 마찬가지입니다. 아무래도 이렇게 생성된 값들이 실수들이고소수점 아래 자리에서까지 서로 똑같은 값이 존재하기는 거의 불가능하다고 보는 것이 맞을 것 같습니다. 그런데, 실전에서는 이런 경우 말고, 난수를 정수의 형태로 얻어야 하는 경우들이 가끔 있습니다. 예를 들어, 0~100의 범위를 갖는 정수값들을 임의로 생성해야 한다고 가정해봅시다. 그러면 다음과 같은 방법을 생각해볼 수 있습니다.


IDL> PRINT, ROUND(RANDOMU(seed, 6)*100)

          66          22          30          99          80          52


즉, RANDOMU로 생성된 실수형 난수가 기본적으로 0~1의 사이에 있기 때문에 여기에 100을 곱한 다음 정수형으로 바꿔주는 방식입니다. 이런 방법을 사용하면 기본적으로 난수이지만 내가 원하는 형태로 임의의 값들을 생성하는 것이 가능합니다. 그런데 이런 방법을 사용하다 보면 다음과 같이 똑같은 숫자가 중복되는 경우가 가끔 나옵니다.


IDL> PRINT, ROUND(RANDOMU(seed, 6)*100)

          29          54          61          38          38          48


아무래도 정수형으로 변환을 한 상태이기 때문에 서로 같은 값이 존재할 가능성이 실수형일 경우에 비해서는 다소 높아진 것이 사실이기 때문입니다. 그런데 만약 이렇게 중복되는 경우 없이 완전히 서로 다른 값들로만 구성된 정수형 난수들을 얻고 싶다면 어떻게 해야 할까요? 이런 경우라면 위와 같은 방법만으로는 해결이 되지 않습니다. 기본적으로 RANDOMU와 같은 난수 발생 함수는 서로 같은 값을 생성하는 경우도 적은 확률이나마 있습니다. 서로 같은 값은 절대 안된다는 단서 자체가 이런 함수내에 들어가 있지 않고, 그런 역할을 하는 키워드도 존재하지 않습니다. 따라서 직접 어느 정도의 프로그래밍을 해줘야 해결이 가능한 이슈일 수 밖에 없습니다. 그래서 이런 문제를 해결하기 위한 예제 프로그램을 다음과 같이 만들어 보았습니다.


PRO test_random_exclusive


n = 8; number of values

ul = 100; upper limit

r1 = ROUND(RANDOMU(seed, n)*ul)

r2 = r1[UNIQ(r1)]

n1 = N_ELEMENTS(r1)

n2 = N_ELEMENTS(r2)

PRINT, 'initial', n1, n2

PRINT, r1

WHILE n2 LT n1 DO BEGIN

  nr = n1 - n2

  rr = ROUND(RANDOMU(seed, nr)*ul)

  r1 = [r2, rr]

  r2 = r1[UNIQ(r1)]

  n1 = N_ELEMENTS(r1)

  n2 = N_ELEMENTS(r2)

ENDWHILE

PRINT, 'final', n1, n2

PRINT, r1


END


이 프로그램에서는 기본적으로 RANDOMU외에 UNIQ라는 함수를 사용하고, WHILE이라는 조건형 반복문을 사용하고 있습니다. UNIQ 함수는 말 그대로 어떤 배열내의 원소값들 중 서로 겹치는 값들이 있을 경우 이것을 하나의 케이스로 퉁치는 역할을 합니다. 즉, 일단 RANDOMU 함수로 생성된 정수형 난수값들에 대하여 중복을 배제한 결과를 얻고, 이 결과가 원래 결과와 동일한가를 체크하면서, 중복값이 발생할 경우에는 이걸 하나로 퉁치고 그 모자라는 갯수만큼 별도의 난수들을 또 생성하여 덧붙이는 방식이다 정도로 이해하시면 됩니다. 이 프로그램을 n=8, ul=100으로 셋팅해서 여러번 반복 실행하다 보면 중복값이 존재하는 케이스가 어쩌다가 발생하는데 이 경우 다음과 같이 처리가 됩니다.


initial           8           7

          73          80          80          84           3          41          11          86

final           8           8

          73          80          84           3          41          11          86          99


즉, 0~100의 범위를 갖는 8개의 정수형 난수값들을 생성했는데 이 안에 80이라는 값이 두 개가 중복되어 발생한 경우, 80은 하나의 값으로 만들어버리고, 그 대신 모자라는 하나의 값을 별도의 난수로 채워넣는 방식으로 최종 결과를 얻었음을 알 수 있습니다. 이와 같은 방법을 사용하면 원하는 범위에 속하는 원하는 갯수만큼의 정수형 난수들을 중복되지 않게 생성할 수 있을 것 같습니다.


LIST