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 parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num 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;
  }
}

 

728x90
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기