본문 바로가기
🎈 활동/🎁 CJ 리모트 인턴십

[Expert] 파이썬 데이터 분석 기본 (개별 과제)

by 엄지잉 2023. 10. 14.

📌 문제 : 로또 번호 생성기

- input 함수로 사용자 입력을 받아, 총 6개의 숫자를 만들어준다.

 

💡 입력

- 모든 자연수 가능, 1번만 입력

- 아무 자연수 6개를 골라, 콤마(',')를 기준으로 순서대로 입력

  ▶ 들어온 입력 처리 → 모든 숫자가 1-45 사이가 되도록 한다.

 

💡 예외

- 생성한 6개의 숫자 중, 중복 존재 → 둘 중 큰 숫자에 +1

- 이때, 45를 넘으면 1로 return

- 만약, number_list 중복된 값이 존재하면 해당 값들의 기존 리스트 값을 비교하고, 기존 값이 더 큰 것에 +1

 

💡 결과

- make_lotto_number 함수에서 number_list return

 

# 실행 예시
>>> 입력 : 4151361,13597019,222,123315,4,13516
>>> 출력 : [45, 17, 38, 35, 4, 39]
    
    
# 실행 예시2
>>> 입력 : 1370519,12970,1,4747475758,8079013768590167835917,22
>>> 출력 : [41, 44, 1, 34, 25, 22]
    
# 실행 예시3
>>> 입력 : 17239407190457, 178278, 1111, 14595971035597, 33, 55
>>> 출력 : [31, 28, 7, 8, 33, 9]
    (원래 [31, 28, 7, 7, 33, 9]인데, 1111보다 14595971035597이 더 크므로 +1을 해줌.)

 

 

문제를 보자마자 처음 든 생각은 이거였다.

(리스트에 중복값이 존재할 때, 굳이 원본 값이랑 비교를 해야하나? 그냥 +1 해주면 되는데 왜 귀찮게..)

 

 

🌐 # 코드 1

def make_lotto_number(string):
    """
    input 함수를 통해 입력받은 변수를 string이라는 parameter로 넘겨줍니다.
    해당 string을 처리하여 6개의 1 ~ 45 사이의 숫자를 가지는 리스트를 만들어서 return 합니다.
    """
    string_list = list(string)
    number_list = []
    num = 0

    for i in range(6):
        num = string_list[i] % 46
        
        print(num)
        if num > 45:
            num = 1
            
        if num in number_list:
            num = num + 1

        number_list.append(num)

    return number_list

# 1 : 4151361,13597019,222,123315,4,13516 => [45, 17, 38, 35, 4, 39]
# 2 : 1370519,12970,1,4747475758,8079013768590167835917,22 => [41, 44, 1, 34, 25, 22]
# 3 : 17239407190457, 178278, 1111, 14595971035597, 33, 55 => [31, 28, 7, 8, 33, 9]
input_num = list(map(int, input().split(',')))
print(make_lotto_number(input_num))

 

오랜만에 파이썬 다루기도 하고 손 풀 겸 대충 짜봤다.

하지만 과제이니.. 하라는 대로 해야지!

 

 

🌐 # 코드 2

def make_lotto_number(string):
    
    string_list = list(string)
    number_list = []
    num = 0

    for i in range(6):
      max = -1
      num = string_list[i] % 46

      if (num > 45 or num < 1):
          num = 1

      # print('num: ', num, '------------------------------------')
      # 중복 제거
      if num in number_list: 
          idx = number_list.index(num)

          if(string[idx] > string[i]): max = idx
          else: max = i

      if (num > 45 or num < 1):
          num = 1

      number_list.append(num)
      # print('전:', number_list[-1])
      if(max > -1):
        # print('max: ', max)
        number_list[max] = number_list[max] + 1
        # print('후:', number_list[max])

      print('number_list: ', number_list, '=======================')
    return number_list

# 1 : 4151361,13597019,222,123315,4,13516 => [45, 17, 38, 35, 4, 39]
# 2 : 1370519,12970,1,4747475758,8079013768590167835917,22 => [41, 44, 1, 34, 25, 22]
# 3 : 17239407190457, 178278, 1111, 14595971035597, 33, 55 => [31, 28, 7, 8, 33, 9]
input_num = list(map(int, input().split(',')))
print(make_lotto_number(input_num))

 

여전히 마음에 안 드는 점이 몇 가지 있었다.

 

1. num > 45일 때, 1로 초기화시켜주는 위치가 마음에 안 든다.

2. 예외(중복 존재 값)를 2가지가 같다고 단정짓지 말고 2가지 이상의 수를 비교했을 때도 잘 되는가?를 실험해봐야 한다.

3. 숫자가 1부터 46까지 나와야 하는데, 46 입력시 나머지가 0이다. (n % 46을 썼으니까) 하지만, 모든 숫자가 1-45 사이로 뽑혀야 하므로 고쳐야 한다.

    3-1. 그냥 % 45로 했다. 리모트에서 제시한 실행 예시랑 출력값이 다르다.

    3-2. 리모트에서 제시한 % 46을 썼다. 만약, 46과 47이 주어졌을 때 & 46을 하면 [0, 1]이 된다. 0은 1-45 사이가 아니니, +1을 해주면 [1, 1]이 되어버리고, 46 < 47 이기 때문에 [1, 2]가 된다. 이때, ex. 49 % 46 = 3인데, 얘는 3으로 그대로 쓰는지? 아니면 앞에 값들을 다 +1 해줬으니, 얘도 +1을 해줘야하는가?에 대한 생각이 들었다.

4. 입력에 같은 숫자가 있는 경우

 

3, 4번에 대한 언급은 따로 없어서, 3번은 0인 것만 +1, 4번은 그냥 무시(숫자를 중복으로 입력하지 않는다는 조건)하기로 했다.

 

 

🌐 # 코드 3

def make_lotto_number(string):
    
    string_list = list(string)
    number_list = []
    num = 0

    for i in range(6):
      max = -1
      num = string_list[i] % 46

      if (num > 45 or num < 1):
          num = 1

      # print('num: ', num, '------------------------------------')
      # 중복 제거
      if num in number_list: 
          idx = number_list.index(num)

          if(string[idx] > string[i]): max = idx
          else: max = i

      if (num > 45 or num < 1):
          num = 1

      number_list.append(num)
      # print('전:', number_list[-1])
      if(max > -1):
        # print('max: ', max)
        number_list[max] = number_list[max] + 1
        # print('후:', number_list[max])

      print('number_list: ', number_list, '=======================')
    return number_list

# 1 : 4151361,13597019,222,123315,4,13516 => [45, 17, 38, 35, 4, 39]
# 2 : 1370519,12970,1,4747475758,8079013768590167835917,22 => [41, 44, 1, 34, 25, 22]
# 3 : 17239407190457, 178278, 1111, 14595971035597, 33, 55 => [31, 28, 7, 8, 33, 9]
input_num = list(map(int, input().split(',')))
print(make_lotto_number(input_num))

 

원래는 중복 제거를 리스트에 넣기 전에 했다. 

이렇게 진행하면 리스트에 다 넣고 중복된 값이 존재할 수 있기 때문에, 리스트에 append를 먼저 한 후 중복 제거를 실시하는 방식으로 코드를 바꿨다.

 

 

🌐 # 코드 4

def make_lotto_number(string):
    
    string_list = list(string)
    number_list = []
    num = 0

    for i in range(6):
      num = string_list[i] % 46
      if (num > 45 or num < 1): num = 1
      number_list.append(num)
    
    # 중복 제거
    for i in range(6):
      max = -1
      for n in range(i):
        if(number_list[i] == number_list[n]):
          if(string_list[i] > string_list[n]): max = i
          else: max = n
          number_list[max] = number_list[max] + 1

      print('number_list: ', number_list, '=======================')
    return number_list

# 1 : 4151361,13597019,222,123315,4,13516 => [45, 17, 38, 35, 4, 39]
# 2 : 1370519,12970,1,4747475758,8079013768590167835917,22 => [41, 44, 1, 34, 25, 22]
# 3 : 17239407190457, 178278, 1111, 14595971035597, 33, 55 => [31, 28, 7, 8, 33, 9]
input_num = list(map(int, input().split(',')))
print(make_lotto_number(input_num))

 

코드 3의 경우 원하는대로 출력되지 않는다는 문제점이 있었다.

입력 : 1, 2, 3, 45, 46, 47

출력 : [1, 2, 3, 45, 0, 1]

 

1. 46 점검 시 : <1, 2, 3, 45, 1> → <1, 2, 3, 45, 2> → <1, 2, 3, 45, 3> → <1, 2, 3, 45, 4>

2. 47 점검 시 : <1, 2, 3, 45, 4, 1> → <1, 2, 3, 45, 4, 2> → <1, 2, 3, 45, 4, 3> → <1, 2, 3, 45, 4, 4> → <1, 2, 3, 45, 4, 5>

 

위의 과정으로 진행되도록 코드를 바꾸니(코드 4) 원하는대로 출력이 잘 되는 것을 확인했다.

 

하지만 또 딜레마에 빠졌다.

- 1, 2, 3, 45, 91, 137 실행 결과 [1, 2, 3, 45, 1, 2]가 출력된다.

number_list[max] = number_list[max] + 1한 값(리스트 내에서 중복제거하는 과정에서 +1을 했을 때)이 45를 넘는다면 1로 초기화된다. 만약 이게 또 안에 있는 값이랑 겹친다면? 

무한 굴레(리스트에 중복되는 값이 없는게 확인 될 때까지 점검)라는 생각이 들었다.

솔직히 말하자면 코드에 for문, if문이 너무 많아서 복잡해 보일 것 같았고, 과제한 다른 사람들한테 물어보니 위의 실행 예시 3개만 맞게 나오는거 확인되면 제출했다고 했다. 별 다른 반례를 생각 안 했다고...

그래서 나도 토요일에 강사님이 풀어주시는 코드를 보기로 하고 그냥 제출했다.

 

🌐 강사님의 코드

def make_lotto_number(string):
    """
    input 함수를 통해 입력받은 변수를 string이라는 parameter로 넘겨줍니다.
    해당 string을 처리하여 6개의 1 ~ 45 사이의 숫자를 가지는 리스트를 만들어서 return 합니다.
    """
    raw_numbers = [int(x) for x in string.split(',')]
    # number_list = [(x % 45) + 1 for x in raw_numbers] # 1 ~ 45
    number_list = [x % 46 for x in raw_numbers] # 0 ~ 45

    # 46의 배수인 숫자들은 0으로 변환이 되기 때문에, 0인 숫자들은 45로 변환.
    for idx in range(len(number_list)):
        if number_list[idx] == 0:
            number_list[idx] = 45

    # 중복 숫자 처리하기
    # --> 중복인 숫자 2개가 원래 가지고 있던 숫자를 비교해서 큰 값이었던 위치에 있는 숫자를 +1 한다.
    # brute-force(전탐색)
    for i in range(len(number_list)):              # i = 1
        for j in range(i+1, len(number_list)):     # j = 2, 3, 4, 5
            while number_list[i] == number_list[j]: # 2개 숫자가 모두 다를때 까지,
                if raw_numbers[i] > raw_numbers[j]:
                    if number_list[i] != 45:
                        number_list[i] = number_list[i] + 1
                    else: # number_list[i] == 45면, 46이 되기 때문에 1로 변환.
                        number_list[i] = 1

                else: # raw_numbers[i] <= raw_numbers[j]
                    if number_list[j] != 45:
                        number_list[j] = number_list[j] + 1
                    else:
                        number_list[j] = 1

    return number_list


input_numbers = input()   ## 사용자 입력(stdin)을 받아서 문자열로 저장.
number_list = make_lotto_number(input_numbers)  ## 처리가 다된 숫자 6개가 담긴 리스트를 전달받음.
print(number_list)

 

강사님께서 반례가 존재하는 것을 알고, 문제를 조금 고쳐오셨다.

- 입력에 중복된 값이 존재하면, 앞에 있는 값에 +1

 

하지만, 여전히 내가 해결하지 못한 위의 문제에 대해서는 이 코드도 마찬가지였다.

- 1, 2, 3, 45, 91, 137 실행 결과 [1, 2, 3, 45, 1, 2]가 출력된다.

마지막 남은 10분 QnA 시간에 질문드렸는데, 강사님께서 이 문제가 아예 잘못된 것일수도 있겠다고 조금 더 고민해보고 알려주신다고 했다.

 

문제를 처음 읽을 때부터 어떻게 코드를 짜야할 지 대강은 잡혔지만, 세세하게 잡히지는 않았다.(위에서 언급한) 내가 고민했던 여러 예외들이 존재했기 때문이다. 다른 사람들처럼 '실행 결과만 잘 나오면 됐지'라는 생각에서 그치지 않고, 2시간 정도 고민해보는 시간을 가졌던 게 나름 값졌다고 생각한다. 만약 마지막 반례를 찾아내지 못했다면, 강사님도 옳은 코드라고 생각하셨을 것이니... 오랜만에 알고리즘 코드 짜면서 생각해보니 나름 흥미로웠다. 열심히 해야지 🔥