프로그래머스 파이썬 기초 해석 :: 분수의 덧셈 (math / fractions 라이브러리)

프로그래머스 URL

https://school.programmers.co.kr/learn/courses/30/lessons/120808


프로그래머스 코딩 카테고리 

코딩 기초 트레이닝

Day 2 사칙연산, 조건문, 배열

Lv.0





문제 설명

첫 번째 분수의 분자와 분모를 뜻하는 numer1, denom1, 두 번째 분수의 분자와 분모를 뜻하는 numer2, denom2가 매개변수로 주어집니다. 두 분수를 더한 값을 기약 분수로 나타냈을 때 분자와 분모를 순서대로 담은 배열을 return 하도록 solution 함수를 완성해보세요






제한 사항

0 <numer1, denom1, numer2, denom2 < 1,000




문제 이해

1. 입력값 해석

solution으로 들어오는 매개변수 4가지

분자 numer1, numer2

분모 denom1, denom2



2. 출력값 해석  

리스트 형태이고 [ 분자, 분모 ] 위치로 반환할 것 

answer = [] 은 출력값 넣을 곳



3. 분수와 분자를 더하기

노트에 써서 일반적으로 분수랑 분모를 더할때 어떤 공식이 있는지를 생각해보면,

분모는 분모끼리 곱해서, 최대공배수로 만들고 

분자는 다른 분모 값이랑 곱한다. 


이를, 변수로 나타내면

[분자] num = numer1 * denom2 + numer2 * denom1 

[분모] den = denom1 * denom2 



4. 기약 분수로 약분을 한 상태로 출력을 해야함 

기약 분수란 분자와 분모 사이에 더 이상 약분할 수 없는 최대공약수가 있는 분수

기약 분수로 만들기 위해서는 분자와 분모의 최대공약수를 구한 후, 각각을 최대공약수로 나눠야한다. 

① 분자와 분모의 최대공약수를 구하기

② 분자와 분모를 최대공약수로 나누기 

이게 문제다... 이를 해결할 수 있는 방법은 바로 아래 "문제점"에서 자세하게,,, 


(참고) 연산기호

슬러시 하나 / 연산은 일반적인 계산 나눗셈으로 나머지가 있을때 소수점 이하로 리턴됨. 

(딱 맞게 떨어지면, 소수점 이하없이 정수로만 반환됨) 

슬러시 두개 // 연산은 나머지는 다 버려지고 몫만 정수로 출력이됨  

% 연산은 몫은 버리고 나머지만 출력됨 




------------------------




* 문제에 대한 정답 코드는 아래에 있는 "문제 해답"에 있고,

"문제점 및 풀이과정"에서는, 해답에 필요한 함수 및 이해 방식들을 적었습니다.


문제점 및 풀이과정 

분자랑 분모를 더한 값을 약분 안하고, 바로 출력하면 아래 사진과 같이 테스트 통과 1개가 안된다. 기약분수로 만들어야하는 것이 관건이다. 



① 분자와 분모의 최대공약수를 구하기

크게 3가지 방법이 있다. 



a) math 모듈에 있는 gcd 최대공약수를 구하는 치트키를 쓰는 방법 

"import math" 를 첫 줄에 입력해서, math 모듈을 쓸 수 있도록 선언해야함 

최대공약수: gcd (Greatest Common Divisor) 

최소공배수: lmc (Least Common Multiple)

 
import math     #math 모듈을 쓰기 위해서 import 필수
c = math.gcd(a, b)    #최대공약수 
c = math.lmc(a, b)    #최소공배수



만약, 다른 문제에서 최소공배수인 lmc()를 써야하는데, 이 함수를 알지 못하더라도, 최대공약수를 활용해서, 최소공배수를 찾을 수 있음 

 
def lcm(a, b):
  return a * b // gcd(a, b) 
두 수의 곱을 최대공약수로 나눈 값이 최소공배수 



b) 수학적으로 푸는 방법 : 두 수가 똑같이 나눠떨어지는 값을 찾는 것 

i) 연산과정 

8과 6이 있을때, 최대공약수를 구할려면 두 수가 똑같이 0으로 되어지는 값을 찾아야한다. 

두 수 중에서 작은 값을 하나하나씩 줄여가면서, 큰 값과 비교하며 나눴을때 나머지가 0이 되는 값을 찾는 것

작은 값을 찾을때는, min() 함수를 활용하면 되고, %연산으로 나머지를 알고, == 연산으로 값이 0과 일치하는지 체크한다.  

이후에, 논리 연산자인 "and"로 두 수의 값이 True/False인지 확인하면 된다. 

※ & 비교 연산자랑 자꾸 헷갈리는데, &는 비트연산할때 쓰는거라서 출력이 0/1이다. 


아래는 전체 코드 중 연산에 해당되는 일부를 추출했다. 

 
min_value = min(a, b)  # 두 수 중에서 작은 값을 선택
if a % i == 0 and b % i == 0:  # 두 수 모두 나누어 떨어지는 수인지 아닌지 체크
    gcd = i  # 맞다면 변수에 저장 



c) 분수 연산을 수행하는 fractions 모듈의 Fraction 클래스 사용하기 

Fraction 클래스는 분수를 나타내는 객체를 생성하고 분수 연산을 수행할 수 있는 기능을 제공한다. 

(fraction은 '분수'라는 뜻) 

"from fractions import Fraction" 를 먼저, 선언을 해야 이 클래스를 사용할 수 있다. 

분수 관련된 기능을 수행하는거라서, 첫번째 함수랑 두번째 함수를 + 연산하면 자동으로 계산이 된다. 

분자랑 분모를 알아낼려면, Fraction 객체의 아래 속성을 활용하면 된다. 

[분자] 변수명.numerator

[분모] 변수명.denominator 



 
from fractions import Fraction

def solution(numer1, denom1, numer2, denom2):
    answer = Fraction(numer1, denom1) + Fraction(numer2, denom2)
    return [answer.numerator, answer.denominator]


+) 반복문 관련

반복문 while문, for문 중에서 for문이 더 적합하다. 

왜냐하면, while 문은 true 일때만 반복하기 때문에 반복문을 멈출려면, false 값을 만들어줘야한다. 

반면에, for문은 range() 함수를 활용해서 반복 범위를 지정할 수 있기에 코드가 더 간결해진다. 

iii) range() 함수 

range()함수는 세가지 인자를 사용해 반복 범위를 설정한다.

range(start, stop, step) 이 풀 형식인데, 1번째 인자와 3번째 인자는 생략이 가능하다.

- start (옵션): 반복의 시작 값이며, 기본값은 0.

- stop (필수): 반복의 종료 값으로 start(혹은 0)부터 stop-1까지의 정수 시퀀스를 생성한다. 3가지 인자 중에서 유일하게 생략이 불가능한 녀석이라 range(5)라고 쓰면, 0부터 4까지 숫자를 만들겠다는 의미 

- step (옵션): 각 반복 단계의 증감 값이며 기본값은 1. 

양수 값이면 증가하고, 음수 값이면 감소한다. start부터 stop-1까지를 step 간격으로 생성

 
min_value = min(a, b)  # 두 수 중에서 작은 값을 선택 
gcd=0 #최대공약수를 담을 변수 선언 

for i in range(min_value, 0, -1):  # 작은 값부터 1씩 감소시키면서 반복
    if a % i == 0 and b % i == 0:  # 두 수 모두 나누어 떨어지는 수를 찾으면
        gcd = i  # 해당 수를 최대공약수로 반환
        break #멈춤 


while 문으로도 가능하긴하다. 

 
i = min(a,b)
gcd = 0 

while i > 0:
    if a % i == 0 and b % i == 0 : 
        gcd = i
        break
    i -= 1 



 

② 분자와 분모를 최대공약수로 나누기 

구한 최대공약수대로 분자랑 분모를 나누면됨 

최대공약수를 gcd로 했을때, 

[분자] num//gcd  (또는) num/gcd 

[분모] den//gcd  (또는) den/gcd 

연산기호는 둘다 가능하나, 이 문제에서는 //을 추천





문제 해답

※ 24.01.19 기준으로, 프로그래머스 변수명이랑 제시된 함수의 매개변수 자리나 변수명은 다른 사람들이 푼 풀이와 미세하게 다른점이 있으니, 주의해서 풀어야한다. 

a) math 모듈의 gcd 최대공약수 함수를 사용하는 경우

 
import math

def solution(numer1, denom1, numer2, denom2):
    num = denom1 * numer2 + denom2 * numer1
    den = denom1 * denom2
    
    gcd = math.gcd(num, den)
    return [num//gcd, den//gcd]

b) 모듈이나 클래스를 안쓰고 수학적으로 푼 방법

1. for문을 사용했을때
 
def solution(numer1, denom1, numer2, denom2):
    num = denom1 * numer2 + denom2 * numer1
    den = denom1 * denom2
    
    gcd = 0
    for i in range(min(den,num), 0, -1):
        if den % i == 0 and num % i == 0:
            gcd = i 
            break
            
    return [num//gcd, den//gcd]


2. while문을 사용했을때 
 
def solution(numer1, denom1, numer2, denom2):
    num = denom1 * numer2 + denom2 * numer1
    den = denom1 * denom2

    i = min(num, den)
    gcd = 0 

    while i > 0 :
        if num % i == 0 and den % i == 0 : 
            gcd = i
            break
        i -= 1

    return [num//gcd, den//gcd]


c) fraction 클래스 사용했을때 

 
from fractions import Fraction

def solution(numer1, denom1, numer2, denom2):
    answer = Fraction(numer1, denom1) + Fraction(numer2, denom2)
    return [answer.numerator, answer.denominator]



------------------------


느낀점

참고풀이를 보면서 따라도 해봤지만, 안되는게 많았다. 문제는 함수의 매개변수, 인자 위치에 따라 값이 틀리게 나오는거였다. 그리고 while문이랑 for문을 어떤 상황에 쓰는게 더 적합한지도 점점 알게되는 것 같다. range()라는 함수가 매개변수를 3개나 가지고 있는지도 이번에 알았다... 분수 연산 자체를 수행하는 클래스도 있는데, 음,, 객체명이 어려워서 잘 쓸 수 있을지는 모르겠다. if 문에 있는 and 논리 연산자랑 &랑 혼동을 했다. 현실에서는 같은 의미이지만, 파이썬에서 &은 전혀 다른 의미니까 주의해서 써야겠다. 









댓글

이 블로그의 인기 게시물

[KT 에이블스쿨 - IT 트랜드] 국내외 AI 관련 규제

KT 에이블스쿨 : 핀테크 아이디어 공모전

KT 에이블스쿨 : 6-7차 미니프로젝트 - 제안서 기반 솔류션 기획 및 설계