품생품사(品生品死)

소프트웨어 품질에 살고 품질에 죽는 그런 평범한 일상 블로그

TESTING/PROGREMING

[파이썬 코딩 - Chap.25] 실습 많이 생각해서 로또 시뮬레이션 만들

품생품사(品生品死) 2020. 11. 29. 18:00
반응형

파이썬 프로젝트 : 로또 시뮬레이션 만들기

😎 로또 시뮬레이션 프로그램을 만들어 보겠습니다.

 

이 프로그램은 과정이 많기 때문에, 여러 파트로 나눠서 문제를 해결해 나갈 건데요.

먼저 이 레슨에서 프로그램 전체에 대한 설명을 한 번 하고 가겠습니다.

 

규칙

로또는 주 1회씩 열립니다.

하지만 한 사람이 한 회차에 여러 번 참여할 수도 있습니다.

 

번호는 1부터 45까지 있는데요.

주최측에서는 매주 6개의 '일반 당첨 번호'와 1개의 '보너스 번호'를 뽑습니다.

그리고 참가자는 1번 참여할 때마다 서로 다른 번호 6개를 선택합니다.

 

당첨 액수는 아래 규칙에 따라 결정됩니다.

  1. 내가 뽑은 번호 6개와 일반 당첨 번호 6개 모두 일치 (10억 원)
  2. 내가 뽑은 번호 5개와 일반 당첨 번호 5개 일치, 그리고 내 번호 1개와 보너스 번호 일치 (5천만 원)
  3. 내가 뽑은 번호 5개와 일반 당첨 번호 5개 일치 (100만 원)
  4. 내가 뽑은 번호 4개와 일반 당첨 번호 4개 일치 (5만 원)
  5. 내가 뽑은 번호 3개와 일반 당첨 번호 3개 일치 (5천 원)

과제 설명

여러분의 임무는 로또 시뮬레이션을 위한 함수들을 작성하는 것입니다.

어떤 함수들이 있는지 봅시다.

generate_numbers

이 함수는 파라미터로 정수 n을 받습니다.

무작위로 1과 45 사이의 서로 다른 번호 n개를 뽑고, 그 번호들이 담긴 리스트를 리턴합니다.

예를 들어서 아래 코드를 실행하면,

print(generate_numbers(6))

이런 결과가 나올 수 있습니다.

[16, 2, 30, 40, 15, 33]

하지만 다시 실행하면 다른 결과가 나오겠죠?

참고로 이 함수는 참가자의 번호를 뽑는 데에도 쓰이고, 보너스를 포함한 당첨 번호 7개를 뽑는 데에도 쓰입니다.

draw_winning_numbers

일반 당첨 번호 6개와 보너스 번호 1개가 포함된 리스트를 리턴합니다.

일반 당첨 번호 6개는 정렬되어 있어야 하고, 보너스 번호는 마지막에 추가하면 됩니다.

 

예를 들어서 아래 코드를 실행하면,

print(draw_winning_numbers())

이런 결과가 나올 수 있습니다.

[4, 12, 14, 28, 40, 41, 6]

앞서 정의한 generate_numbers 함수를 잘 활용하면, 함수를 간결하게 작성할 수 있습니다.

count_matching_numbers

파라미터로 리스트 list_1과 리스트 list_2를 받고, 두 리스트 사이에 겹치는 번호 개수를 리턴합니다.

 

예를 들어서 아래 코드를 실행하면,

print(count_matching_numbers([2, 7, 11, 14, 25, 40], [2, 11, 13, 14, 30, 35]))

2, 11, 13이 겹치기 때문에 이렇게 나옵니다.

3

또 아래 코드를 실행하면,

print(count_matching_numbers([2, 7, 11, 14, 25, 40], [14]))

14가 겹치기 때문에 이렇게 나오겠죠?

1

check

참가자의 당첨 금액을 리턴합니다.

파라미터로 참가자가 뽑은 번호가 담긴 리스트 numbers와 주최측에서 뽑은 번호가 담긴 리스트 winning_numbers를 받는데요.

 

numbers는 당연히 번호 여섯 개를 담고 있고, winning_numbers는 보너스까지 해서 번호 7개를 담고 있겠죠?

 

예를 들어서 아래 코드를 실행하면,

numbers_test = [2, 4, 11, 14, 25, 40]
winning_numbers_test = [4, 12, 14, 28, 40, 41, 6]

print(check(numbers_list, winning_numbers_test))

4, 14, 40... 이렇게 번호 3개가 겹치기 때문에 5천 원에 당첨됩니다.

5000

 

로또 시뮬레이션 : 번호 뽑기

로또 시뮬레이션 프로그램을 한 단계씩 완성해 나갑시다.

먼저 main.py 파일의 generate_numbers 함수를 작성하세요.

generate_numbers

이 함수는 파라미터로 정수 n을 받습니다. 무작위로 1과 45 사이의 서로 다른 번호 n개를 뽑고, 그 번호들이 담긴 리스트를 리턴합니다.

 

예를 들어서 아래 코드를 실행하면,

print(generate_numbers(6))

이런 결과가 나올 수 있습니다.

[16, 2, 30, 40, 15, 33]

하지만 다시 실행하면 다른 결과가 나오겠죠?

 

참고로 이 함수는 참가자의 번호를 뽑는 데에도 쓰이고, 보너스를 포함한 당첨 번호 7개를 뽑는 데에도 쓰입니다.

[해설]

1과 45 사이의 번호 n개를 무작위로 뽑아야 하는데요.

우선 빈 리스트를 만드는 것부터 시작합시다.

numbers = []

우리는 총 n개의 번호를 추가하고 싶은 거니까, numbers 리스트에 값이 n개 미만인 동안 반복문을 돌겠습니다.

while len(numbers) < n:

그리고 while문의 수행 부분에서 리스트에 번호를 추가하면 되는데요. 번호를 무작위로 뽑는 건 randint 함수를 사용해서 할 수 있겠죠?

while len(numbers) < n:
    num = randint(1, 45)

이제 여기서 조금 주의하셔야 할 게 있습니다.

이 new_number를 무작정 추가하면, 리스트에 중복값이 들어갈 수도 있습니다.

new_number가 numbers 리스트에 없는 경우에만 추가를 해야겠죠?

while len(numbers) < n:
    num = randint(1, 45)
    if num not in numbers:
        numbers.append(num)

반복문에서 나오면 numbers는 번호 n개가 들어 있을 텐데, 이제 numbers를 리턴하기만 하면 됩니다.

return numbers

[모범 답안]

from random import randint


def generate_numbers(n):
    numbers = []

    while len(numbers) < n:
        num = randint(1, 45)
        if num not in numbers:
            numbers.append(num)

    return numbers

# 예시 결과 출력
print(generate_numbers(6))

 

로또 시뮬레이션 : 당첨 번호 뽑기

main.py 파일의 draw_winning_numbers 함수를 작성하세요.

draw_winning_numbers

일반 당첨 번호 6개와 보너스 번호 1개가 포함된 리스트를 리턴합니다.

일반 당첨 번호 6개는 정렬되어 있어야 하고, 보너스 번호는 마지막에 추가하면 됩니다.

 

예를 들어서 아래 코드를 실행하면,

print(draw_winning_numbers())

이런 결과가 나올 수 있습니다.

[4, 12, 14, 28, 40, 41, 6]

앞서 정의한 generate_numbers 함수를 잘 활용하면, 함수를 간결하게 작성할 수 있습니다.

[해설]

우선 일반 번호 7개를 뽑을게요. 이건 그냥 generate_numbers 함수를 써서 하면 되겠죠?

winning_numbers = generate_numbers(7)

7개의 요소 중, 첫 6개는 일반 당첨 번호고 마지막 1개는 보너스 번호입니다.

 

그러면 첫 6개만 정렬하면 되겠죠?

첫 6개만 정렬하는 방식은 여러 가지가 있을 텐데요.

 

그 중 한 가지 방법만 보여드릴게요.

  1. 리스트 슬라이싱을 이용해서, 일반 당첨 번호(첫 6개)와 보너스 번호(마지막 1개)를 분리한다.
  2. sorted 함수를 사용해서 일반 당첨 번호만 정렬한다.
  3. 일반 당첨 번호와 보너스 번호를 다시 합친다.

이걸 코드로 옮기면 이렇게 됩니다.

winning_numbers = generate_numbers(7)
return sorted(winning_numbers[:6]) + winning_numbers[6:]

[모범 답안]

from random import randint


def generate_numbers(n):
    numbers = []

    while len(numbers) < n:
        new_number = randint(1, 45)
        if new_number not in numbers:
            numbers.append(new_number)

    return numbers


def draw_winning_numbers():
    winning_numbers = generate_numbers(7)
    return sorted(winning_numbers[:6]) + winning_numbers[6:]

 

로또 시뮬레이션 : 겹치는 번호 개수

main.py 파일의 count_matching_numbers 함수를 작성하세요.

count_matching_numbers

파라미터로 리스트 list_1과 리스트 list_2를 받고, 두 리스트 사이에 겹치는 번호 개수를 리턴합니다.

 

예를 들어서 아래 코드를 실행하면,

print(count_matching_numbers([2, 7, 11, 14, 25, 40], [2, 11, 13, 14, 30, 35]))

2, 11, 13 3개가 겹치기 때문에 이렇게 나옵니다.

3

또 아래 코드를 실행하면,

print(count_matching_numbers([2, 7, 11, 14, 25, 40], [14]))

14만 겹치기 때문에 이렇게 나오겠죠?

1

[해설]

일단 count라는 변수를 정의하고, 초깃값을 0으로 설정하겠습니다.

count = 0

count는 파라미터로 받은 두 리스트에서 총 몇 개의 번호가 겹치는지 세는 용도입니다.

 

기본 로직

이제 리스트의 번호들을 보면서 비교를 하면 될 텐데요.

이것을 어떤 로직으로 할 수 있을까요?

  1. list_1의 각 원소를 본다.
  2. 해당 원소가 list_2에 있는지 확인한다.
  3. 만약 보고 있는 list_1의 원소가 list_2에도 있으면 count를 1 늘린다.

이렇게 하면 될 텐데요.

 

코드 작성

list_1의 각 원소를 보는 것은 반복문을 통해 할 수 있습니다.

for num in list_1:

그리고 현재 보고 있는 원소인 num이 list_2에 있는지 확인하는 것은 리스트의 in 키워드를 통해 할 수 있습니다.

for num in list_1:
    if num in list_2:
        count += 1

반복문이 끝나면, 누적된 count 값을 리턴하면 되겠죠?

[모범 답안]

def count_matching_numbers(list_1, list_2):

  # 코드를 작성하세요.

  same_numbers = list(set(list_1).intersection(list_2))

  return len(same_numbers)

# 테스트

print(count_matching_numbers([2, 7, 11, 14, 25, 40], [2, 11, 13, 14, 30, 35]))

print(count_matching_numbers([2, 7, 11, 14, 25, 40], [14]))

 

로또 시뮬레이션 : 당첨금 확인

main.py 파일의 check 함수를 작성하세요.

 

당첨금 규칙

참고로 당첨 액수는 아래 규칙에 따라 결정됩니다.

  1. 내가 뽑은 번호 6개와 일반 당첨 번호 6개 모두 일치 (10억 원)
  2. 내가 뽑은 번호 5개와 일반 당첨 번호 5개 일치, 그리고 내 번호 1개와 보너스 번호 일치 (5천만 원)
  3. 내가 뽑은 번호 5개와 일반 당첨 번호 5개 일치 (100만 원)
  4. 내가 뽑은 번호 4개와 일반 당첨 번호 4개 일치 (5만 원)
  5. 내가 뽑은 번호 3개와 일반 당첨 번호 3개 일치 (5천 원)

check

참가자의 당첨 금액을 리턴합니다.

파라미터로 참가자가 뽑은 번호가 담긴 리스트 numbers와

주최측에서 뽑은 번호가 담긴 리스트 winning_numbers를 받는데요.

 

numbers는 당연히 번호 여섯 개를 담고 있고,

winning_numbers는 보너스까지 해서 번호 7개를 담고 있겠죠?

[예시1]

예를 들어서 아래 코드를 실행하면,

numbers_test = [2, 4, 11, 14, 25, 40]
winning_numbers_test = [4, 12, 14, 28, 40, 41, 6]

print(check(numbers_test, winning_numbers_test))

4, 14, 40... 이렇게 번호 3개가 겹치기 때문에 5천 원에 당첨됩니다.

5000

[예시2]

그리고 아래 코드를 실행하면,

numbers_test = [2, 4, 11, 14, 25, 40]
winning_numbers_test = [2, 4, 10, 11, 14, 40, 25]

print(check(numbers_test, winning_numbers_test))

일반 번호 2, 4, 11, 14, 40... 이렇게 5개가 겹칩니다. 그리고 보너스 번호 25도 겹치죠? 그러면 5천만 원에 당첨됩니다.

50000000

[해설]

일치하는 번호 개수 세기

당첨금을 알기 위해서는 두 가지를 봐야 합니다.

  1. 참가자 번호 6개와 일반 당첨 번호 6개 중 몇 개가 일치하는지
  2. 참가자 번호 6개와 보너스 번호 1개 중 몇 개가 일치하는지

각각을 코드로 나타내면 이렇습니다.

count = count_matching_numbers(numbers, winning_numbers[:6])
bonus_count = count_matching_numbers(numbers, winning_numbers[6:])

이해되시나요? winning_numbers의 첫 6개는 일반 당첨 번호고 마지막 1개 번호는 보너스 번호이기 때문에, 이렇게 쓸 수 있습니다.

 

당첨금 리턴하기

이제 count와 bonus_count에 따른 당첨금을 구해서 리턴하면 됩니다. 다양한 경우를 봐야 하기 때문에 if, elif, else의 조합으로 작성하겠습니다.

if count == 6:
    return 1000000000
elif count == 5 and bonus_count == 1:
    return 50000000
elif count == 5:
    return 1000000
elif count == 4:
    return 50000
elif count == 3:
    return 5000
else:
    return 0

[모범 답안]

from random import randint


def generate_numbers(n):
    numbers = []

    while len(numbers) < n:
        new_number = randint(1, 45)
        if new_number not in numbers:
            numbers.append(new_number)

    return numbers


def draw_winning_numbers():
    winning_numbers = generate_numbers(7)
    return sorted(winning_numbers[:6]) + winning_numbers[6:]


def count_matching_numbers(numbers, winning_numbers):
    count = 0

    for num in numbers:
        if num in winning_numbers:
            count = count + 1

    return count


def check(numbers, winning_numbers):
    count = count_matching_numbers(numbers, winning_numbers[:6])
    bonus_count = count_matching_numbers(numbers, winning_numbers[6:])

    if count == 6:
        return 1000000000
    elif count == 5 and bonus_count == 1:
        return 50000000
    elif count == 5:
        return 1000000
    elif count == 4:
        return 50000
    elif count == 3:
        return 5000
    else:
        return 0


# 테스트
print(check([2, 4, 11, 14, 25, 40], [4, 12, 14, 28, 40, 41, 6]))
print(check([2, 4, 11, 14, 25, 40], [2, 4, 10, 11, 14, 40, 25]))

 

로또 시뮬레이션 : 합치기

이제 필요한 함수를 모두 작성했습니다.

 

만약 모든 함수가 올바르게 동작한다면 사실상 다 끝난 것입니다.

여러분이 작성한 함수들을 모두 lottery.py라는 파일에 넣어 주세요.

 

우리는 이 파일을 '모듈'로써 활용할 것입니다.

그리고 lottery_driver.py 라는 파일은 그냥 제공해 드릴 건데요.

 

lottery_driver.py
0.01MB

 

 

이 파이썬 코드는 lottery.py모듈에 있는 함수들을 사용해서

로또 100장 시뮬레이션을 한 후, lottery.html이라는 파일을 생성합니다.

 

그러기 위해서는 우선 lottery.py와 lottery_driver.py를 같은 폴더에 넣어 주세요.

그리고 나서 lottery_driver.py 파일을 실행해 보세요.

그러면 아래 이미지에 나온 것처럼, lottery.html 파일이 생성될 것입니다.

 

HTML 파일은 웹사이트에 그림을 그려 주는 역할이라고 생각하시면 되는데요.

이 파일은 시뮬레이션 결과를 브라우저에서 시각적으로 보여 줍니다.

 

This is coding_0001
브라우저로 열기

PyCharm을 사용하고 계신다면, lottery.html을 오른쪽 클릭한 후,

Open in Browser에 마우스를 올리고, 원하는 브라우저를 선택하세요.

모든 게 올바르게 되었다면, 브라우저에 아래와 같은 결과가 나올 것입니다.

 

This is coding_0002
예제 화면

만약 제대로 나오지 않는다면, 앞선 과제들을 보며 함수가 제대로 작성되었는지 확인해 보세요.

고생하셨습니다!

 

 

Related References

 

코딩이 처음이라면, 코드잇

월 3만원대로 Python, JavaScript, HTML/CSS, Java 등 1,600개 이상 프로그래밍 강의를 무제한 수강하세요

www.codeit.kr:443

This is coding_000
PYTHON 프로그래핑

요약 : sparta coding club, 스파르타 코딩, 코드잇, 노마드 코더, 프로그래밍, 직장인 코딩, 내일 배움 카드 코딩, 밀크티 코딩, 초등 코딩, 아이스크림 코딩, 코딩 소프트웨어, 파이썬 국비 지원, 파이썬 교육

728x90
반응형