본문 바로가기
카테고리 없음

로또 당첨 번호 출현 빈도 계산하기1

by 경자꿈사 2024. 7. 11.

지금까지 어떤 번호가 1등 당첨 번호에 가장 많이 나왔는지 직접 프로그램을 만들어서 확인해 보자.

우선 역대 1등 당첨 번호를 알아야 하는데, 예전에는 로또 공식 사이트에서 역대 당첨 번호를 엑셀 파일로 다운로드 받을 수 있었던 거 같은데 지금 확인해 보니 찾을 수가 없었다.

그래서 네이버에 조회해 보니 너무나 고맙게도 해당 파일을 다운로드 받을 수 있는 블로그가 있었다.

다운로드 받은 엑셀 파일에는 로또 회차 정보, 당첨자 수 등 여러 가지 정보가 자세히 나와 있었는데 실제로 내가 필요한 건 1등 당첨 번호 6 개이기 때문에 필요한 부분만 복사해서 메모장에 붙여 넣어 새 파일을 만들었다.

로또당첨번호.txt
0.02MB

 

이제 '로또당첨번호.txt' 라는 파일의 내용을 내가 작성할 프로그램에서 한 줄씩 읽어와 숫자별로 카운트를 해주면 될 거 같다. 우선 텍스트 파일의 내용을 한 줄씩 읽어 오는 것 부터 알아보자. 

 

File 클래스의 foreach 메서드를 사용하면 되는데 인수 값으로 파일의 경로 및 이름을 주고 (아래의 그림처럼 해당 파일이 있는 폴더의 위치에서 irb 를 실행했다면 경로 없이 파일명만 적어 주면 된다.) 뒤에는 파일에서 한 줄씩 읽어 온 문자열을 갖고 해야 할 작업을 중괄호( { } ) 또는 do...end 안에 작성하면 된다. Ruby에서는 이것을 블록이라고 한다. 이때 해당 문자열을 전달 받을 변수를 두 개의 '|' (파이프라인) 사이에 적어 주면 되는데 메서드의 파라미터 역할을 한다고 생각하자. 아래 코드는 단순하게 파일의 내용을 한 줄 한 줄 그대로 화면에 출력하는 작업을 하고 있다.

>> File.foreach("로또당첨번호.txt") { |str| puts str }
6       16      34      37      39      40
1       7       21      30      35      38
10      12      29      31      40      44
13      14      22      26      37      38
6       7       13      28      36      42
17      26      29      30      31      43
3       20      28      38      40      43
12      16      21      24      41      43
14      33      34      35      37      40
1       12      16      19      23      43
=> nil

그런데 우리가 해야 할 일은 단순히 파일의 내용을 화면에 출력하는 게 아니라 읽어온 문자열에서 각각의 숫자들을 추출하고 각 숫자들의 발생 횟수를 카운트해야 한다.

우선 문자열에서 각각의 숫자들을 추출해 보자. 파일에서 읽어 온 문자열을 보면 숫자와 숫자 사이에 공백이 보이는데 이 공백(문자)들을 구분자(delimiter)로 하여 문자열을 나누면 숫자(로 된 문자열)들을 추출해 낼 수 있을 것이다.

split 메서드가 바로 우리가 원하는 기능을 해주는 메서드이다. 다음 그림을 보면 split 메서드가 문자열에서 숫자(로 된 문자열)들만 추출해서 배열에 담아 돌려주는 것을 볼 수 있다.

split 메서드는 문자열 또는 정규표현식(다음에 기회가 있을 때 설명하겠다.)을 인수로 받아 그 인수를 구분자로 하여 문자열을 나누는데 아래 코드처럼 인수를 생략하면 공백 문자를 기본 구분자로 사용하여 문자열을 나누게 된다.

이때의 공백 문자는 스페이스와 탭 그리고 개행문자를 가리킨다. 이전 글에서 문자열 내에서 개행문자를 "\n" 로 표현한다고 얘기했는데 탭문자는 "\t" 로 공백 문자는 "\s" 로 표현하면 된다.

>> str = "6 16 34 37 39 40"
=> "6 16 34 37 39 40"
>> str.split
=> ["6", "16", "34", "37", "39", "40"]
>> str2 = "A,B,C,D,E"
=> "A,B,C,D,E"
>> str2.split
=> ["A,B,C,D,E"]
>> str2.split(",")
=> ["A", "B", "C", "D", "E"]

str2 변수에 담긴 문자열 "A,B,C,D,E" 는 인수로 "," 를 줘야 원하는 결과 값을 얻는 걸 볼 수 있다.

이제 각 숫자들의 출현 횟수를 카운트해 보자. 각 숫자에 대해 현재까지의 출현 횟수를 저장해 놓을 곳이 필요한데 가장 원시적인 방법은 1부터 45까지의 각 숫자별로 변수를 만드는 거다. 그런데 이 방법은 45개의 변수를 만들어야 되는 것 뿐만 아니라 해당 변수의 값을 증가시키기 위해 또 그 만큼의 조건문까지 작성해야 한다는 문제도 가지고 있다.

단순히 코드가 길어서가 아니라 이런 식으로 작성하게 되면 프로그램을 수정해야 할 때 고쳐야 할 부분이 많아지고 그에 따라 실수할 가능성도 높아지게 된다.

 

num1 = 0
num2 = 0
num3 = 0
...
num45 = 0

생략...

if num == 1
  num1 += 1
elsif num == 2
  num2 += 1
elsif num == 3
  num3 += 1
...
elsif num == 45
  num45 += 1
end

 

다음으로 생각해 볼 수 있는 것이 배열을 사용하는 방법이다. 이전 글에서 설명했듯이 배열은 0부터 시작하는 요소의 위치 값(인덱스)을 사용하여 원하는 위치에 값을 넣거나 가져올 수 있다.

숫자는 1부터이고 인덱스는 0부터가 시작이므로 배열의 크기를 45개가 아닌 46개로 하면 해당 숫자 그대로를 인덱스로 사용할 수 있다. 그리고 배열 생성 시 요소의 초기값은 모두 0으로 채워 넣어야 하므로 [0, 0, 0, 0, 0, ... 0 ] 이렇게 0 을 46 개  적어 넣어서 작성할 수도 있지만 [0] * 46 처럼 간단하게 작성하는 방법도 제공한다.

>> arr = [0] * 46
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
>> arr.size
=> 46
>> arr[0]
=> 0
>> arr[45]
=> 0
>> arr[46]
=> nil

위에서 만든 배열의 크기는 46 이므로 젤 큰 인덱스는 45 이다. 그래서 arr[46] 은 결과가 0이 아니라 nil 이다. Ruby 와 달리 이렇게 잘못된 인덱스를 사용할 때 에러를 발생시키는 언어들도 있다. 이러한 차이는 해당 언어가 갖는 특성일 뿐 좋고 나쁨의 차이는 아니다. 물론 여러 언어를 같이 사용해야 하는 개발자라면 해당 언어가 갖는 특성들을 잘 알고 있어야 한다.

아래 그림을 보면 Node.js 는 Ruby 와 비슷하게 에러 대신 undefined를 돌려주는 게 보이고 Python 은 이름만 봐도 어떤 에러인지 알 수 있는 IndexError를 던지고 있다.

다음 글에서는 실제로 배열을 사용해서 각 숫자들의 출현 빈도를 계산하는 코드를 작성해 보겠다.
See you again~~