수학적 개념을 코드로 구현하는 문제이다. 이 문제를 볼때 간단하게 풀 수 있을줄 알았는데, 머리로 알고 있던 수학적 개념을 코드로 옮기는 과정에서 막혔다. 내가 막혔던 부분은 분수를 덧셈하고 분모, 분자를 출력하는 것까진 성공했지만, 약분하는 과정을 생각하지 못했다. 즉, 약분해야할 최대공약수를 구하는 부분에서 막혔던 것이다.
<오답코드>
import java.util.*;
class Solution {
public int[] solution(int numer1, int denom1, int numer2, int denom2) {
int[] answer = {};
// 통분할 분수의 분모
int totalDenom = 0;
// 첫번째 분모가 클 경우
if(denom1 > denom2){
// 큰 값의 분모에서 작은 값 분모를 나눈 나머지가 0일때
if((denom1 % denom2) == 0){
// 통분해야할 분모값은 둘 중 큰 값으로 한다.
totalDenom = denom1;
}else{
// 큰 값의 분모에서 작은 값 분모를 나눈 나머지가 0이 아닐때
// 통분해야할 분값은 두 분모의 곱이다.
totalDenom = denom1 * denom2;
}
}else{
if((denom2 % denom1) == 0){
totalDenom = denom2;
}else{
totalDenom = denom1 * denom2;
}
}
// 통분할 분수의 분자
int totalNum = (numer1 * (totalDenom / denom1)) + (numer2 * (totalDenom / denom2));
List<Integer> arr = new ArrayList<>();
arr.add(totalNum);
arr.add(totalDenom);
// 가변길이의 배열 답안을 int[] 배열에 담는 로직을 익히기 위해 일부로 넣은 코드.
answer = arr.stream().mapToInt(Integer::new).toArray();
return answer;
}
}
<정답코드>
import java.util.*;
class Solution {
public int[] solution(int numer1, int denom1, int numer2, int denom2) {
int[] answer = {};
int totalDenom = 0;
if(denom1 > denom2){
if((denom1 % denom2) == 0){
totalDenom = denom1;
}else{
totalDenom = denom1 * denom2;
}
}else{
if((denom2 % denom1) == 0){
totalDenom = denom2;
}else{
totalDenom = denom1 * denom2;
}
}
int totalNum = (numer1 * (totalDenom / denom1)) + (numer2 * (totalDenom / denom2));
// 최대공약수 구하는 로직 익히기!
int max = 1;
for(int i = 1; i <= totalNum && i <= totalDenom; i++){
if(totalNum % i == 0 && totalDenom % i == 0){
max = i;
}
}
List<Integer> arr = new ArrayList<>();
arr.add(totalNum / max);
arr.add(totalDenom / max);
answer = arr.stream().mapToInt(Integer::new).toArray();
return answer;
}
}
<수정한 정답코드>
import java.util.*;
class Solution {
public int[] solution(int numer1, int denom1, int numer2, int denom2) {
int[] answer = {};
List<Integer> arr = new ArrayList<>();
// 통분하기 위해 두 분모를 곱함
int totalDenom = denom1 * denom2;
// 분수 통분
int totalNumer = (numer1 * denom2) + (numer2 * denom1);
// 최대공약수를 구하는 로직
int max = 1;
for(int i = 1; i <= totalDenom && i <= totalNumer; i++){
if(totalDenom % i == 0 && totalNumer % i == 0){
max = i;
}
}
// 기약분수로 만들기 위해 최대공약수로 나눔.
totalDenom = (int)totalDenom / max;
totalNumer = (int)totalNumer / max;
// 정답리스트에 add
arr.add(totalNumer);
arr.add(totalDenom);
// int[]형 배열로 변환
answer = arr.stream().mapToInt(Integer::new).toArray();
// 리턴
return answer;
}
}
<코드설명>
먼저 분수의 덧셈을 위한 통분 과정을 구현했다. numer1, numer2 둘 중 어떤 숫자가 클지 모르기 때문에 if문으로 각 케이스에 대해 분기처리했고, 분모1과 분모2를 나눈 나머지가 0일때 분모1이 크면 분모1로 통분하면 된다. 분모1과 분모2를 나눈 나머지가 0이 아닐때는 분모1과 분모2를 곱한 값으로 통분한다.
+ 24.08.14 수정) 최대공약수로 약분하는 로직이 존재하므로 굳이 denom1과 denom2의 크기를 비교해서 통분 분모를 만들 필요가 없다. 따라서, 코드를 더 간략하게 수정했다.
분자는 통분한 분모값과 원래 분모값을 나눈 값을 분자에 곱해주면 된다. 예를 들면 1/7 + 1/8일때 1/7은 56으로 통분될때 분자에 56 / 7을 한 8을 곱해주면 되고, 1/8은 56으로 통분될때 분자에 56 / 8을 한 7을 곱해주면 되는 것이다.
최대공약수는 for문으로 돌려야한다. 통분한 분자와 분모 중 어느 값이 더 클지 모르므로 i <= totalNum && i <= totalDenom 조건으로 loop를 돌며, i는 1부터 1씩 증가하며 totalNum과 totalDenom 둘 다 0으로 나눠떨어지는 값 중 최대값을 찾아낸다.
// 최대공약수를 구하는 로직
int max = 1;
for(int i = 1; i <= totalDenom && i <= totalNumer; i++){
if(totalDenom % i == 0 && totalNumer % i == 0){
max = i;
}
}
마지막으로, 그냥 int[]형 answer 배열에 바로 값을 넣으면 되지 왜 굳이 ArrayList를 사용하여 한번 더 값을 넣느냐?
프로그래머스를 풀며, 정답 배열의 크기가 딱 정해져있지 않는 상황에서 꼭 int[] 형으로 반환해야하는 문제가 많았다. 이 로직을 몰라서 헤맨적이 많았기에, 해당 로직을 익히려는 의도에서 ArrayList를 사용하여 가변길의 정답을 대응하였고, 반환은 int[] 형으로 하는 과정을 거쳤다.
아직 좋은 성능, 깔끔한 코딩을 하는 실력은 낮지만, 더욱 열심히 풀며 개선해나가야겠다!
'알고리즘 공부' 카테고리의 다른 글
[프로그래머스] 옷가게 할인 받기 - JAVA (0) | 2024.08.12 |
---|---|
[프로그래머스] 기능개발 - JAVA (0) | 2024.08.10 |
[파이썬] 백준 5522 - 카드 게임 (0) | 2021.08.27 |
[파이썬] 백준 3046 - R2 (0) | 2021.08.27 |
[파이썬] 2914 - 저작권 (2) | 2021.08.27 |