파이썬 랜덤(random) 모듈로 난수 생성하기

랜덤한 숫자를 뽑아내는 알고리즘은 프로그래머가 따로 만들 필요가 없습니다. 파이썬의 경우는 random모듈을 사용해서 랜덤한 수를 뽑아낼 수 있는데 여기에 사용되는 알고리즘 엔진은 메르센 트위스터(Mersenne Twister) 생성기라는 것입니다. 임의의 값을 뽑아내는 데 이 생성기를 많이 사용하죠. 이것을 의사난수 생성기(Pseudo-random)이라고 하는데 의사라는 말은 가짜라는 뜻으로 추측이 가능하다는 말입니다. 가짜라고 해서 품질이 나쁜건 아닙니다. 

메르센 트위스터는 품질이 좋은 의사난수를 생성해 내며 사용자 입장에서는 정말 난수처럼 보입니다. 단지 엄밀히 따지자면 수학적인 계산을 통해서 만들어진 수이므로 f:x->y 처럼 함수관계가 있다는 것 뿐입니다. 


저도 잘은 모르지만 난수를 생성하는 엔진(아날로그)을 seed값으로 사용하기 위해 컴퓨터에 연결해서 난수를 생성하는 방법도 있다고 합니다. 연구하시는 분들이 쓰시겠죠. 일반적인 경우는 그냥 random을 사용하면 됩니다. 게임 분야에서도 난수생성을 밥먹듯이 하는데 이 때도 의사난수를 생성해서 사용합니다.


random 모듈의 대략적인 구조

▲random모듈을 import하고 random.ranom()이라고 하면 0부터 1사이의 난수가 생성됩니다. 그냥 사용하는 것보다는 random.random이 무엇인지 알아 보겠습니다. 이 부분이 이해 안되시면 다음으로 그냥 넘어가서 사용법만 알아두어도 무방합니다.


▲random 모듈 내부에는 Random이라는 클래스가 정의되어 있습니다. 사용자가 random모듈을 import하는 순간 Random클래스는 자동적으로 인스턴스화 됩니다. 쉽게말해서 Random클래스의 객체가 하나 생성되는 거죠.


▲_inst가 바로 Random클래스의 객체입니다. Random객체에는 random이라는 메소드가 있습니다. 이 녀석이 난수를 생성하죠. 

random=_inst.random

이렇게 해서 random이라는 이름으로 이 메소드를 바인딩(binding) 해서 우리는 _inst.random 대신 random이라고 사용하는 것이죠.

코드를 보면 다른 메소드들 역시 같은 방식으로 반인딩 되었습니다. 역시 우리는 이 메소드들을 바인딩된 이름으로 사용하는 것입니다.


random.seed

앞서 말했듯이 의사난수는 함수처럼 입력값이 있어야 난수를 뽑아낼 수 있습니다. 이를 위해서 seed를 세팅하는 작업을 하는데 seed는 말 그대로 씨앗이라는 뜻입니다. (씨앗을 넣어주면 여기에서 랜덤한 열매가 열리는 것이죠.) 이 작업이 난수 생성을 위해 첫번째로 해야할 초기화 작업입니다.


random.seed(1)로 seed를 1로 세팅하고 난수를 생성하면 항상 0.1343642441124012라는 값이 나오게 됩니다. 세번 연속으로 seed값을 1로 초기화 해보니 생성되는 난수는 항상 같습니다. seed값을 2로 바꾸면 생성되는 난수 값도 바뀌게 됩니다.


앞서 seed를 설정하지 않고 random.random()을 실행했을 때는 매번 다른 난수가 생성되었습니다. seed를 설정하지 않으면 기본적으로 현재 시간을 seed로 사용합니다. 시간은 계속 흘러가므로 예측하기 힘드니 seed값으로 꽤 적절하기 때문이죠.


▲seed값으로 문자열, bytes 또는 bytearray가 사용될 수 있습니다. 이 값들은 int값으로 변환되어 seed로 사용됩니다.


random.random

이미 눈에 익었죠? random.random()은 0부터 1사이의 부동소수를 랜덤으로 만들어냅니다.

[0, 1)

[는 폐구간을 뜻합니다. )는 개구간을 뜻하죠. 그래서 범위는 0<= 난수 < 1이 됩니다.


random.uniform(a, b)

a <= N <= b  범위에서 부동소수 난수 N을 생성합니다.

random.random()과 같은 동작을 하겠죠.



random.randint(a, b)

이름 대로 a와 b 사이의 랜덤한 정수를 생성합니다.



random.choice(seq)

이 함수는 인수로 받은 seq(시퀀스 자료형)에서 하나를 선택합니다. 


random.randrange([start[, stop[, step]]])

이 것은 내부적인 구현은 다르지만 다음과 동일한 동작을 합니다.

random.choice(range(start, stop, step)))


random.sample(population, k)

random.choice와 비슷한데 choice는 시퀀스에서 하나만 뽑아냈다면 sample은 k개 만큼 뽑아냅니다.


random.shuffle(x)

리스트 x의 항목의 순서를 랜덤하게 섞습니다.

튜플이나 문자열 같은 시퀀스 객체는 섞을 수 없습니다. 이 객체는 immutable(불변)객체니까요.


random.getrandbits(n)

n자리 bit를 랜덤 생성합니다. 반환값은 10진수입니다.
예를들어 n의 값이 2라면 이진수 0부터 11 까지의 수를 랜덤 생성합니다.


random.triangular([low,[high,[mode]]])

앞서 random.random()과 random.uniform(a, b)의 생성난수는 균등분포를 갖는 난수를 생성합니다. 난수 생성 범위 내에 임의의 두 수에 대해서 이 두 수의 생성확률이 같다는 말입니다. 예를들어 0~1까지 범위에서 랜덤한 수를 생성한다면 0.2132131123이 나올 확률은 0.87978972983이 나올 확률과 같습니다.


random.triangular은 균등분포가 아닌 삼각 분포입니다. 

low와 high를 범위를 갖는 난수를 생성하되 mode값을 경계로 좌우 대칭의 삼각분포를 갖습니다. mode값에 가까울 수록 분포가 많고 멀어질 수록 낮아지는 그래프입니다. 따라서 mode에 가까운 수가 생성확률이 높겠죠.


  • mode가 0인 경우

0에 가까운 수들이 많이 보이네요.


  • mode가 1인 경우

1에 가까운 수의 비율이 높습니다.


이 밖에도 다양한 분포들이 있는데 여기서는 이 정도만 소개하겠습니다.


기억해 둘 점은 랜덤 모듈은 처음에 설명했듯이 예측이 가능한 의사난수를 생성합니다. 따라서 보안키를 생성하거나 보안을 위한 용도로 사용하는 것은 안전하지 않습니다.

이 댓글을 비밀 댓글로
  1. 랜덤함수 찾아보다가, 정리 잘 되어 있어서, 좋은 참고하고 갑니다~