여태까지 배운 내용을 바탕으로, 파이썬으로 로또 번호 6개를 중복 없이 뽑는 방법을 알아보겠습니다.

 


 c언어 스럽게 짠다면 아래와 같이 작성할 수 있습니다.

 

 

 먼저, flag를 선언합니다. 이것은 단지, 어떤 수가 뽑혔는지를 나타내는 배열입니다. 5번째 줄부터 6번을 돌려보는데요. r은 구간 [1, 45+1)에 속하는 수 중 하나입니다. 수를 뽑았을 때, flag[r]의 값이 1이라면 루프를 계속 돌고, 그렇지 않으면 r을 선택합니다. r을 선택할 때, flag[r]에 1을 넣어줍니다. 그리고, 선택한 수를 추가해 주면 됩니다.

 

 

 이렇게 6번을 뽑으면 됩니다. 중복된 수가 나오지 않았음을 볼 수 있습니다. 그런데, 파이썬에는 딕셔너리가 있습니다. 이것은, 중복된 키 값을 저장하지 않습니다. 그렇다면, 아래와 같이 로직을 짜셔도 됩니다.

 

 

 보시면, flag에 들어있는 원소 갯수가 6개가 될 때 까지 계속 while loop를 돈다는 것을 알 수 있습니다. flag가 딕셔너리이기 때문에, 이미 뽑힌 수를 넣으면, flag에 들어가지 않습니다. 따라서, 루프를 빠져나온 후에, 플래그에 들어있는 수의 개수는 6개가 됩니다. 이것을 list로 변환해서 출력하면 됩니다.

 

 

 결과 값이 잘 나옴을 볼 수 있습니다.

 


 random을 잘 보면, shuffle과 sample, choices와 같은 것들이 있음을 볼 수 있습니다. 이 중에서 3번째 메서드는 다음에 언급할 기회가 있으면 하도록 하겠습니다. 사실 저는 처음에, shuffle을 시키는 것을 생각했습니다. 이 방법이 나쁜 아이디어는 아닙니다.

그런데, sequence의 크기가 매우 크고, 뽑아야 할 수가 10 미만이라고 해 봅시다. 이 때 매번 shuffle을 시키는 게 효율적일까요?

 

 

 위 프로그램을 보면, 배열의 크기를 각각 10만, 100만, 1000만, 1억으로 주었음을 알 수 있습니다. 다음에, 7번째 줄에서 random의 셔플 메서드를 호출하는데요. 6번째 줄과 8번째 줄에 시간을 측정하는 함수가 있으니, 셔플 메서드가 수행될 때 걸리는 시간을 구하려고 하는 것일 겁니다.

 

 결과부터 보겠습니다.

 

 크기가 10배 증가할 때 대략 10배 가량 수행 시간이 늘어남을 볼 수 있습니다. 저 메서드가 100만번 호출되고, 대상 배열의 크기가 10만 정도라면, 시간이 매우 오래 걸릴 겁니다. 루프를 돌 때 마다 O(len(배열)) 만큼이 걸리기 때문입니다. 다시 이 프로그램의 목적을 생각해 봅시다. 우리는 단지 45개의 수 중 랜덤하게 6개를 선택하면 됩니다. 배열 전체 혹은 일부를 셔플하는 것이 아닙니다. 적절한 셔플 사용 예는 이 글에 나와 있습니다. 이 글은 이를 이용해서 ps에 적용하는 법도 다루고 있으니, 읽어보시면 좋을 듯 싶습니다.

 

 그러면 뭘 쓰는 게 좋을까요? 문제 상황을 다시 요약해 보면, 수가 45개가 있고, 이 중에 6개를 중복 없이 선택한다고 하였습니다. 적절한 함수는 문서를 잘 읽어보면 sample임을 알 수 있습니다. 이 메서드를 이용해서 로또 번호를 출력하는 프로그램을 만들어 보도록 하겠습니다.

 

 

 2번째 줄에, range 객체를 넣어서 1부터 45까지를 순서대로 배열에 넣었습니다. 다음에, 20번 loop를 돌면서 랜덤의 sample 메서드를 호출하는데요. 순서 객체가 1부터 45까지 있는 리스트이고, 이 중 6개를 sampling을 합니다.

 

 

 결과는 위와 같습니다.

 


 리스트의 일부분만 셔플을 할 수 없을까요? shuffle 메소드가 넘겨받은 리스트를 변형시킵니다. 그리고, 리턴값이 없습니다. 그래서 전처리를 복잡하게 해야 하는데요. sample을 쓰면 복잡하지 않습니다. 아래는 구간 [s, e)를 셔플하는 프로그램입니다.

 

 

 s와 e의 값을 적절히 잘 조절해 줍니다. 핵심이 되는 부분은 7번째 줄입니다. [0, s) 구간과 [e, len(arr)) 구간은 그냥 arr을 얻어옴을 볼 수 있어요. 중요한 것은, 중간에 있는 rd.sample입니다. 여기에 arr[s:e]를 넣었는데요. 이것은 arr의 [s, e)구간을 의미합니다. 이 구간에 들어있는 수의 개수는 e-s개인데, 이게 음수가 되면 0이 되어야 하므로, k값에 0과 e-s의 max 값을 취했습니다.

 

 즉, shuffle_sub 메서드는 [s, e) 구간을 셔플합니다.

 

 

 프로그램의 수행 결과는 위와 같습니다.