프로그래머스 파이썬 기초 해석 :: 분수의 덧셈 (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
연산기호는 둘다 가능하나, 이 문제에서는 //을 추천
문제 해답
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) 모듈이나 클래스를 안쓰고 수학적으로 푼 방법
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]
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 논리 연산자랑 &랑 혼동을 했다. 현실에서는 같은 의미이지만, 파이썬에서 &은 전혀 다른 의미니까 주의해서 써야겠다.
댓글
댓글 쓰기