본문 바로가기
개발자 일기/알고리즘 공부

[Python]백준#28702-FizzBuzz

by MS_developer 2025. 2. 5.

 

 

백준에서 브론즈 1 등급의 문제 FizzBuzz를 풀어가는 과정에서 좋은 코드를 발견하여 동작 원리와 핵심 코드를 분석해 보았다.

내가 직접 작성한 코드는 굉장히 1차원적이라 좀 더 나은 방안들을 고려하다가, 상위 랭킹에 bennyws 님의 코드가 매우 간결하고 효율적이라 생각하여 포스팅을 작성했다.


문제

FizzBuzz 문제는 i=1,2,⋯ 에 대해 다음 규칙에 따라 문자열을 한 줄에 하나씩 출력하는 문제입니다.

  •  의 배수이면서 의 배수이면 “FizzBuzz”를 출력합니다.
  •  의 배수이지만 의 배수가 아니면 “Fizz”를 출력합니다.
  •  의 배수가 아니지만 의 배수이면 “Buzz”를 출력합니다.
  •  의 배수도 아니고 의 배수도 아닌 경우 를 그대로 출력합니다.

FizzBuzz 문제에서 연속으로 출력된 세 개의 문자열이 주어집니다. 이때, 이 세 문자열 다음에 올 문자열은 무엇일까요?

입력

FizzBuzz 문제에서 연속으로 출력된 세 개의 문자열이 한 줄에 하나씩 주어집니다. 각 문자열의 길이는 8 이하입니다. 입력이 항상 FizzBuzz 문제에서 연속으로 출력된 세 개의 문자열에 대응됨이 보장됩니다.

출력

연속으로 출력된 세 개의 문자열 다음에 올 문자열을 출력하세요. 여러 문자열이 올 수 있는 경우, 아무거나 하나 출력하세요.

예제 입력

Fizz
Buzz
11

예제 출력

Fizz

내가 작성한 코드

def is_fizz_buzz(num):
    """
    숫자를 받아서 Fizz, Buzz, FizzBuzz를 출력하는 함수
    - 3의 배수면 "Fizz"
    - 5의 배수면 "Buzz"
    - 3과 5의 공배수면 "FizzBuzz"
    - 위 조건이 아니면 숫자 그대로 출력
    """
    if num % 3 == 0 and num % 5 == 0:
        print("FizzBuzz")
    elif num % 3 == 0:
        print("Fizz")
    elif num % 5 == 0:
        print("Buzz")
    else:
        print(num)

def predict_fizz_buzz(a, b, c):
    """
    입력된 값 a, b, c를 확인하여 다음 숫자의 FizzBuzz 상태를 예측하는 함수
    - a, b, c 중 가장 먼저 숫자로 변환 가능한 값을 찾고
    - 해당 숫자에 특정 값을 더한 뒤 FizzBuzz 여부를 확인하여 출력
    """

    def convert_to_int(value):
        """문자열이 정수로 변환 가능하면 변환, 아니면 그대로 반환"""
        try:
            return int(value)  # 정수 변환 성공하면 반환
        except ValueError:
            return value  # 변환 불가능하면 원래 값 그대로 반환

    # 입력값을 정수로 변환 시도
    a, b, c = convert_to_int(a), convert_to_int(b), convert_to_int(c)

    # 첫 번째로 숫자인 값을 찾아 다음 수 예측
    if isinstance(a, int):  # a가 숫자라면
        is_fizz_buzz(a + 3)  # 3을 더한 후 FizzBuzz 판단
        return
    elif isinstance(b, int):  # b가 숫자라면
        is_fizz_buzz(b + 2)  # 2를 더한 후 FizzBuzz 판단
        return
    elif isinstance(c, int):  # c가 숫자라면
        is_fizz_buzz(c + 1)  # 1을 더한 후 FizzBuzz 판단
        return

# 사용자 입력 받기
a = input()
b = input()
c = input()

# FizzBuzz 예측 실행
predict_fizz_buzz(a, b, c)

 

굉장히 직관적이지만, 1차원적이라고 생각했다. 

알고리즘 풀이 자체에는 문제가 없지만, 코드가 지나치게 길다고 생각했다.

 

그래도 제출 자체는 성공했다.

 

먼저 연속된 세 개의 숫자 (x, x+1, x+2)가 항상 Fizz, Buzz, FizzBuzz로만 구성될 수 없다(불가능하다)는 것을 유추해낼 수 있었다.

 

그 근거는 다음과 같다:

 

  • 모든 연속된 세 숫자가 Fizz, Buzz, FizzBuzz만으로 구성될 수는 없다
  • 어떤 세 숫자든 적어도 하나는 일반 숫자(즉, 아무 조건에도 해당되지 않는 값)가 나온다
  • 즉, "Fizz", "Fizz", "Buzz" 또는 "Buzz", "Fizz", "FizzBuzz" 같은 조합이 항상 가능한 것은 아니다

 

이후 이를 기반으로 주어진 3개의 연속된 문자열들(i.e. Fizz, 4, Buzz) 중 하나는 무조건 정수 int 일 것이라는 전제 하에 로직을 구성했다. 

 

주어진 연속된 세 수 a, b, c를 모두 받아왔고, 이를 기반으로 각 수들이 정수인지, 정수라면 입력받은 순서에 따라 값들(3 또는 2 또는 1)을 더한 후 FizzBuzz 함수의 논리를 적용시켰다. 

 

예제의 입력값과 출력값을 정직하게 따른 방법이지만, 당연하게도 매우 비효율적이라고 생각이 들었다.

 

하지만 어떻게 해야 세 번의 입력을 받으면서 빠르게 정답을 도출하고 출력받을 수 있는지 생각이 잘 들지 않았고, 앞서 언급한 대로 다른 사람들의 코드 제출 결과를 확인했다.


분석할 코드

bennyws 님의 코드로, 상위 랭킹 2등에 등록되어 있다.

 

for i in range(3, 0, -1):  # i = 3, 2, 1 (3번 반복)
    x = input()  # 사용자 입력 받기
    if x not in ['Fizz', 'Buzz', 'FizzBuzz']:  # 입력값이 Fizz, Buzz, FizzBuzz가 아니라면
        n = int(x) + i  # 숫자로 변환 후 i를 더함
        print('Fizz'*(n % 3 == 0) + 'Buzz'*(n % 5 == 0) or n)  # FizzBuzz 판별 후 출력
        break  # 반복문 종료 (첫 번째 숫자 입력에서만 실행)

 

먼저 코드의 동작 원리와 핵심 동작으로 구분해 보았다.

 

동작 원리

 

1. 반복문 ( for i in range (3,0,-1) )

  • i 값이 3, 2, 1로 반복 (즉, 총 3번 입력을 받음)
  • 만약 x가 "Fizz", "Buzz", "FizzBuzz"라면 입력을 다시 받음

2. 숫자 입력을 받았을 때

  • x가 정수라면, int(x) + i 를 수행하여 새로운 값을 계산
  • Fizz, Buzz, FizzBuzz 로직에 따라 해당 수를 판별 후 출력
  • break로 루프 종료 (첫 번째 숫자를 입력받은 순간 바로 종료)

 

핵심 동작

 

n = int(x) + i

  • 입력값 x가 정수라면, 현재 루프의 i 값을 더함
  • 즉, 가장 먼저 나오는 숫자가 입력값보다 i만큼 증가한 값으로 변환

print('Fizz'*(n % 3 == 0) + 'Buzz'*(n % 5 == 0) or n)

  • 기존의 FizzBuzz 연산을 계산
    • n % 3 == 0이면 'Fizz' 출력
    • n % 5 == 0이면 'Buzz' 출력
    • 둘 다 참이면 'FizzBuzz' 출력
    • or 연산자를 활용해 Fizz나 Buzz가 없으면 n을 출력

 

3번의 반복, 정수 여부를 간단하게 파악한 후 FizzBuzz 로직을 별도의 함수 없이 적용하는 것까지 매우 깔끔했다.

 

특히 a, b, c의 여부에 따른 1~3의 수를 더한 후 다음 수를 예측하는 방식을 반복문에 포함하고 내림차순으로 3~1까지 예측되는 수에 더하는 방식이 아주 효율적이라고 생각했다.

댓글