Persistent Bugger, CodeWars Kata 자바 솔루션
Persistent Bugger, CodeWars Kata
Java 8 이후 새로 등장한 API들을 익히는 데 코딩 테스트가 매우 유용하네요. 예전에 프로그래머스에서는 자바 솔루션을 작성하다가 배열 핸들링이 불편한 것에 짜증 나서 파이썬으로 문제를 풀곤 했었는데요. 이제 자바에서도 편리한 방법을 제공하는 만큼 얼른 익혀서 자바 8 할 줄 안다고 자신 있게 말해야겠습니다.
Persistent Bugger
www.codewars.com/kata/55bf01e5a717a0d57e0000ec/java
Write a function,
persistence
, that takes in a positive parameternum
and returns its multiplicative persistence, which is the number of times you must multiply the digits innum
until you reach a single digit.For example:
persistence(39) == 3 // because 3*9 = 27, 2*7 = 14, 1*4=4
// and 4 has only one digit
persistence(999) == 4 // because 9*9*9 = 729, 7*2*9 = 126,
// 1*2*6 = 12, and finally 1*2 = 2
persistence(4) == 0 // because 4 is already a one-digit number
양의 정수를 입력으로 받아서 입력받은 숫자를 하나씩 곱합니다. 곱한 값도 2자리 수 이상이라면 다시 곱해서 1자리 수 결과가 나올 때까지 몇 번이나 곱할 수 있는지를 알아내는 함수를 작성해야 합니다.
예시를 살펴 보면 39의 경우 3*9가 첫 번째 곱이 되겠죠. 계산 값은 27이 나옵니다. 그럼 2와 7을 다시 곱해봐야겠죠. 그럼 14가 나오네요. 이것도 두 자릿수 이상이니 다시 1과 4를 곱합니다. 결과가 4가 나오면 한 자릿수로 더 이상 곱을 할 수 없으므로 여기서 멈춥니다. 총 3번을 곱할 수 있었으니 결과는 3이 됩니다.
My Solution
입력값이 한 자리수라면 처음부터 계산할 값이 없기 때문에 0을 반환해야 합니다. 저는 이 상황을 예외로 보고 입력값이 10 미만일 때 0을 반환하도록 예외 코드를 추가로 작성했습니다.
Depth를 알 수 없는 계산이 필요하면 재귀함수를 활용해야 합니다. 저는 multiply(long n, int count)
라는 재귀 함수를 만들었습니다. 입력값을 받아 몇 번 계산할 수 있는지 확인하기 위해 int count
라는 파라미터를 하나 더 받도록 구성했습니다.
숫자끼리 곱하기 위해서 우선 숫자를 String.valueOf()
를 이용해 문자열로 변환했습니다. 그 다음 split("")
으로 잘라줍니다. split()
에 공백 문자열 ""
을 파라미터로 주면 한 글자씩 배열에 담아줍니다. 예전 버전에서는 이렇게 하면 배열 0번째 요소에 공백 문자열이 담겨있어 별도 처리를 해주어야 했지만 지금은 깔끔하게 문자열부터 담긴 배열이 반환됩니다.
Stream.mapToInt
를 이용해서 잘린 배열의 문자열 값들을 숫자로 다시 변환해주었고 이를 int배열로 담아서 곱을 처리했습니다. 이때 1의 값을 가진 long value
라는 변수를 이용해서 배열의 첫번째 요소를 별도 처리하지 않고 곱을 얻었습니다.
계산 결과가 9 를 초과, 즉 두 자리 수라면 다시 함수를 호출하면서 count를 1 증가하도록 해 최종 결괏값을 얻어냈습니다.
package com.codewars;
import java.util.Arrays;
public class Persist {
public static int persistence(long n) {
if (n < 10)
return 0;
return multiply(n, 1);
}
public static int multiply(long n, int count) {
int[] numbers = Arrays.stream(String.valueOf(n).split("")).mapToInt(Integer::parseInt).toArray();
long value = 1;
for (int num : numbers) {
value *= num;
}
if (value > 9) {
count = multiply(value, count + 1);
}
return count;
}
}
Best Practice 1
BP 또한 재귀 함수를 이용합니다. 하지만 저처럼 별도 함수를 만드는 것이 아니라 주어진 함수 하나로 해결했네요. return persistence(m) + 1;
을 이용해서 재귀 함수가 호출될 때마다 카운트가 1 증가하도록 만들었습니다. 똑똑한 재귀함수 사용법이지요.
아래 for 문을 눈여겨봐야 합니다. long
으로 입력된 n
을 문자열로 변환하지 않고 곱하기 위해서 증감 부분이 r /= 10
으로 설정되어 있습니다. 첫 번째 요소에 대한 곱을 예외 처리하지 않기 위해서 1로 초기화된 별도 변수를 활용하는 것은 저와 동일하네요.
class Persist {
public static int persistence(long n) {
long m = 1, r = n;
if (r / 10 == 0)
return 0;
for (r = n; r != 0; r /= 10)
m *= r % 10;
return persistence(m) + 1;
}
}
Best Practice 2
재귀 함수를 만들지 않고 스트림을 이용한 솔루션도 있습니다. Long.toString()
을 통해서 문자열을 만든 다음 chars()
로 IntStream
을 만듭니다. 그런 다음 reduce() 함수에서
곱을 했네요. while문의 조건이 n >= 10
이라서 별도의 예외처리도 필요하지 않다는 게 좋네요. char 값을 곱할 때 int 값처럼 취급하기 위해서 i - '0'
을 이용한 것을 눈여겨봐야겠습니다.
class Persist {
public static int persistence(long n) {
int times = 0;
while (n >= 10) {
n = Long.toString(n).chars().reduce(1, (r, i) -> r * (i - '0'));
times++;
}
return times;
}
}