본문 바로가기

개발일지/TIL

TIL 23-04-21 페어 프로그래밍 - 연속된 수의 합

1. 페어 프로그래밍 - 연속된 수의 합 

 문제점

연속된 세 개의 정수를 더해 12가 되는 경우는 3, 4, 5입니다.
두 정수 num과 total이 주어집니다. 연속된 수 num개를 더한 값이 total이 될 때, 
정수 배열을 오름차순으로 담아 return하도록 solution함수를 완성해보세요.

# num은 숫자의 개수, total은 숫자의 합

# reutrn은 숫자의 리스트

# 배열은 오름차순으로 정렬

 시도해 본 것들

  1. 기준점을 위한 중간값 찾기  <- total // num
    중간값이 정수가 아닌 경우를 위해 조건문 설정 <- num이 홀수인 경우 
  2. 찾은 기준점을 기준으로 시작점 찾기 <- 중간값 - 숫자의 길이//2로 전체 길이의 절반만큼 앞으로 가서 시작 찾기
  3. for문을 range(시작점, 시작점+num)으로 설정
  4. else: #짝수인 경우를 시작지점 구하기 <- 중간값으로 두 개를 구함 total // num, total // num +1
  5. 짝수인 경우의 시작값 찾기 <- (totall // num + 1) - (num // 2) total // num+1 하나로 시작값 찾기 가능했음

 해결 방법

처음에 완성된 코드 

def solution(num, total):
    # num은 숫자의 갯수 , total은 숫자의 합
    # reutrn은 숫자의 리스트
    # 배열은 오름차순으로 정렬
    answer = []
    if num % 2 != 0:
        center = total//num
        start = center - num//2
        
        for i in range(start,start+num):
            answer.append(i)
        return answer
    else:
        right = total//num + 1
        start = right - num//2
        
        for i in range(start,start+num):
            answer.append(i)
        
        return answer

 

코드리뷰 때 본 다른 팀원들의 코드

def solution(num, total):
    # start = 연속된 정수의 수 중 가장 작은값
    answer = []
    start = total//num -(num-1)//2
    for i in range(start, start+num):
        answer.append(i)

    return answer

 

if문 없이 //로 0.5를 내리는 효과를 통해서 홀수, 짝수를 모두 처리할 수 있음을 알았다.

 

내가 이해하기 쉽게 리팩토링 했을 때

def solution(num, total):
    answer = []
    center = round(total/num)
    start = center - num//2

    for i in range(start, start+num):
        answer.append(i)
    return answer

 

# 번외 

처음 만들었을 때 코드 

def solution(num, total):
    # while문으로 +1씩 계속 증가
    # for문으로 num만큼 반복
    # count의 정확한 초기값을 어떻게 주지?
    count = -50
    while True:
        answer = []
        sum_num = 0
        
        for i in range(count, count+num):
            sum_num += i
            answer.append(i)
        if total == sum_num:
            return answer
        count += 1

 

시간 복잡도 O(n²)이고 count 시작값의 설정 문제가 있음

 알게 된 점

range를 변수 + 변수로 처음 써보면서 생소한 느낌이 들었지만, 계속 쓰고 결과를 이해하니 기존에 알고 있는 range의 사용법과 다를 게 없다는 것을 알았다.

주어진 상황에 따라서 조건문이 없더라도 다른 상황을 처리할 수 있음을 알았다.

2. 백준 알고리즘 문제 - 단어 공부 

 문제점

문제
알파벳 대소문자로 된 단어가 주어지면, 
이 단어에서 가장 많이 사용된 알파벳이 무엇인지 알아내는 프로그램을 작성하시오. 
단, 대문자와 소문자를 구분하지 않는다.

입력
첫째 줄에 알파벳 대소문자로 이루어진 단어가 주어진다. 
주어지는 단어의 길이는 1,000,000을 넘지 않는다.

출력
첫째 줄에 이 단어에서 가장 많이 사용된 알파벳을 대문자로 출력한다.
단, 가장 많이 사용된 알파벳이 여러 개 존재하는 경우에는 ?를 출력한다.

# 입력 input()

# 출력 최빈값을 대문자로 출력, if 최빈값이 여러 개면? 출력

 시도해 본 것들

 

1. 입력을 받기 위한 input() 

alpah = input()

 

2. 각각의 단어를 구분하기 위한 for문 사용, 대소문자 구분을 없애고 출력을 위해서 upper()로 통일 

for a in alpah.upper():

 

3. 최빈값을 세기 위한 딕셔너리 사용

alpah_dict = {}
for a in alpah.upper():
    if a not in alpah_dict:
            alpah_dict[a] = 1
        else:
            alpah_dict[a] += 1

for 문으로 각 단어가 처음 등장했다면 if문으로 새롭게 딕셔너리 생성, 이미 존재하면 값 변화 

 

4. 딕셔너리 값 기준으로 정렬하기 <- 딕셔너리. sort() 사용 불가 다른 방법 사용

sorted_dict = sorted(alpah_dict.items(), reverse=True,
                     key=lambda value: value[1])

 sort와 다르게 원본을 조작하지 않기 때문에 변수로 선언해서 사용, 알파벳과 빈도수 모두 필요하므로. items(), 가장 내림차순 정렬을 위한 reverse, 정렬 기준을 위한 lambda 사용

 

5. 동일한 최빈값 존재 체크 

if sorted_dict[0][1] != sorted_dict[1][1]:
        print(sorted_dict[0][0])
    else:
        print('?')

여기까지 하고 제출을 시도했을 때 런타임 에러 발생, 코드 흐름을 따라가다 주어진 알파벳이 1종류일 때 if문에서 에러 발생 찾음


6. 예외 처리 

try:
    if sorted_dict[0][1] != sorted_dict[1][1]:
        print(sorted_dict[0][0])
    else:
        print('?')
except IndexError:
    print(sorted_dict[0][0])

 해결 방법

alpah = input()
alpah_dict = {}
for a in alpah.upper():
    if a not in alpah_dict:
        alpah_dict[a] = 1
    else:
        alpah_dict[a] += 1
sorted_dict = sorted(alpah_dict.items(), reverse=True,
                     key=lambda value: value[1])
try:
    if sorted_dict[0][1] != sorted_dict[1][1]:
        print(sorted_dict[0][0])
    else:
        print('?')
except IndexError:
    print(sorted_dict[0][0])

 알게 된 점

작성한 lambda는 이해가 되는데 아직 lambda 자체를 어렵게 생각하는지 사용하려고 마음을 먹기가 쉽지 않다. 

try, except를 사용하면서 어떤 에러가 발생하는지 정확히 알고 원하는 값을 제대로 출력할 수 있었다. 그리고 무사히 반례를 찾은 것은 기쁘지만 제출 전에 스스로 찾을 수 있었으면 더 좋았겠다.

 

2. 백준 알고리즘 문제 - 더하기 사이클 

 문제점

문제
0보다 크거나 같고, 99보다 작거나 같은 정수가 주어질 때 다음과 같은 연산을 할 수 있다. 
먼저 주어진 수가 10보다 작다면 앞에 0을 붙여 두 자리 수로 만들고, 각 자리의 숫자를 더한다. 
그 다음, 주어진 수의 가장 오른쪽 자리 수와 앞에서 구한 합의 가장 오른쪽 자리 수를 이어 붙이면 새로운 수를 만들 수 있다.
다음 예를 보자.
26부터 시작한다. 2+6 = 8이다. 새로운 수는 68이다. 6+8 = 14이다. 새로운 수는 84이다. 
8+4 = 12이다. 새로운 수는 42이다. 4+2 = 6이다. 새로운 수는 26이다.

위의 예는 4번만에 원래 수로 돌아올 수 있다. 따라서 26의 사이클의 길이는 4이다.

N이 주어졌을 때, N의 사이클의 길이를 구하는 프로그램을 작성하시오.

입력
첫째 줄에 N이 주어진다. N은 0보다 크거나 같고, 99보다 작거나 같은 정수이다.

출력
첫째 줄에 N의 사이클 길이를 출력한다.
# 입력 0~99의 정수
# 출력 숫자 사이클의 길이
# while

 시도해 본 것들

# str형

1. 기본적인 구조 작성

num = input()
end_num = num
count = 0
while True:
    count += 1
    
    if num == end_num:
        break
print(count)

 

2. 일의 자릿수와 십의 자릿수 분리하기 

split_num1, split_num2 = num[0], num[1]

 

3. 새로운 숫자 만들기

tens_digit = split_num2
unit_digit = str(int(split_num1) + int(split_num2))
num = tens_digit + unit_digit[-1]

여기까지 진행하고서 num의 값을 1의 자리로 주는 순간 IndexError를 뱉으면서 코드가 정상적으로 작동하지 않았다.
고민을 해도 코드로 구현할 방법이 생각나지 않아서 코드를 완전히 갈아엎었다.

미완성 코드 

num = input()
end_num = num
count = 0
while True:
    count += 1
    split_num1, split_num2 = num[0], num[1]
    tens_digit = split_num2
    unit_digit = str(int(split_num1) + int(split_num2))
    num = tens_digit+unit_digit[-1]
    if num == end_num:
        break
print(count)

 

# int형

4. 기본적인 구조 작성 

num = int(input())
end_num = num
count = 0
while True:
    count += 1
    
    if num == end_num:
        break
print(count)

num의 값을 변화시키면서 같은 값이 다시 되는 경우 while문을 탈출하는 구조 

 

5. 일의 자릿수와 십의 자릿수 분리하기 

tens_digit = num // 10
unit_digit = num % 10

num을 10으로 나눈 몫 == 십의 자리, num을 10으로 나누고 남은 나머지 == 일의 자리 

 

6. 새로운 숫자 만들기

new_tens_digit = unit_digit * 10
new_unit_digit = (tens_digit+unit_digit) % 10
num = new_tens_digit + new_unit_digit

새로운 십의 자리 = 기존 일의 자리이므로 * 10, 새로운 일의 자리 = (기존 십의 자리 + 기존 일의 자리)의 일의 자리 

 

# str형으로 다시 풀기

7. 조건문 설정

if int(num) < 10:
        num = str(int(num) * 11)

값은 제대로 넣었는데 일의 자리가 무한루프에 빠지기 시작함


8.  무한 루프 해결하기 

num = str(int(tens_digit+unit_digit[-1]))
# num = tens_digit+unit_digit[-1] # 앞자리 0 붙는 것 때문에 안됨

주석의 코드는 일의 자리가 들어가면 십의자리 값에 0이 포함되면서 숫자가 이상해졌다. 그래서 int로 바꾸고 다시 str로 바꾸는 방법으로 0을 제거했다. 

 해결 방법

# int형 완성

num = int(input())
end_num = num
count = 0
while True:
    count += 1
    tens_digit = num // 10
    unit_digit = num % 10
    new_tens_digit = unit_digit * 10
    new_unit_digit = (tens_digit+unit_digit) % 10
    num = new_tens_digit + new_unit_digit
    if num == end_num:
        break
print(count)

일의 자리인 경우 0을 붙여 두 자릿수를 만들고 진행해야 하는데 num * 11을 하는 것과 동일한 결과다. 그리고 num * 11은 new_unit_digit + unit_digit과 같은데 tens_digit의 값이 0이기 때문에 new_unit_digit+ new_unit_digit 또한 동일하다.

결과적으로 이렇게 작성하면 num이 한자리 수인 경우를 고려해서 추가 작업을 할 필요가 없다.

 

# str형 완성 

num = input()
end_num = num
count = 0
while True:
    count += 1
    if int(num) < 10:
        num = str(int(num) * 11)
    else:
        split_num1, split_num2 = num[0], num[1]
        tens_digit = split_num2
        unit_digit = str(int(split_num1) + int(split_num2))
        num = str(int(tens_digit+unit_digit[-1]))
    if num == end_num:
        break
print(count)

 알게 된 점

추가 조건인 줄 알았던 부분이 오히려 난이도를 낮추기 위한 방법이었던 것 같다. 

문제가 제시하는 길을 잘 알아채려면 코드를 작성하기 전 주석으로도 제대로 작성 가능해야 함을 다시 깨닫게 됐다.
코드를 완성하고 다시 문제를 읽어봤을 때 처음에는 몰랐던 의미를 문제를 읽었을 때 알아차린 것을 처음부터 생각할 수 있다면 시행착오를 크게 줄일 수 있을 것 같다. 그리고 그것을 위해 꾸준히 풀고 알고리즘 구조도 공부를 해야 할 텐데 이론은 아직 어렵다.