문제

https://www.codewars.com/kata/5a8d2bf60025e9163c0000bc

 

Codewars: Achieve mastery through coding challenge

Codewars is a coding practice site for all programmers where you can learn various programming languages. Join the community and improve your skills in many languages!

www.codewars.com

 

문제 설명

In this Kata, you will sort elements in an array by decreasing frequency of elements. If two elements have the same frequency, sort them by increasing value.

Solution.sortByFrequency(new int[]{2, 3, 5, 3, 7, 9, 5, 3, 7});

// Returns {3, 3, 3, 5, 5, 7, 7, 2, 9}
// We sort by highest frequency to lowest frequency.
// If two elements have same frequency, we sort by increasing value.

More examples in test cases.

Good luck!

 

배열에 integer 값들 count순서대로 정렬하고, count가 같다면 오름차순 정렬하라는 뜻

 

 

 

 

어떻게 풀까?

  • 정렬 문제라서 무지성으로 sort() 메소드를 써야겠다라는 생각을 함
  • 근데 counting순서로 정렬을 할 수 있나..?? sort() 메소드 쓰기전에 배열의 값들 counting을 해야할 것 같음
  • 일단 Map에 (int, count)으로 데이터를 모으고 얘네를 count순으로 정렬하고 배열에 담는 식으로 하면 될 것 같음
  • 드가자

 

 

 

 

코드

자바 코드

package codewars._6kyu.Simple_frequency_sort_20220117;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Solution {

  public static int[] sortByFrequency(int[] array) {
    Map<Integer, Integer> countingMap = getCountingMap(array);
    List<Entry<Integer, Integer>> entryList = getSortedEntryList(countingMap);
    List<Integer> result = getListSortedByFrequency(entryList);

    return result.stream().mapToInt(x -> x).toArray();
  }

  private static Map<Integer, Integer> getCountingMap(int[] array) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int target : array) {
      map.put(target, map.getOrDefault(target, 0) + 1);
    }
    return map;
  }

  private static List<Entry<Integer, Integer>> getSortedEntryList(
      Map<Integer, Integer> countingMap) {
    List<Entry<Integer, Integer>> entryList = new ArrayList<>(countingMap.entrySet());
    entryList.sort((o1, o2) -> {
      if (o1.getValue().equals(o2.getValue())) {
        return o1.getKey() - o2.getKey();
      }
      return o2.getValue() - o1.getValue();
    });
    return entryList;
  }

  private static List<Integer> getListSortedByFrequency(List<Entry<Integer, Integer>> entryList) {
    List<Integer> result = new ArrayList<>();
    for (Entry<Integer, Integer> entry : entryList) {
      int key = entry.getKey();
      int value = entry.getValue();
      for (int i = 0; i < value; i++) {
        result.add(key);
      }
    }
    return result;
  }

  public int[] sortByFrequencyBestPracticeSolution(int[] array) {
    Map<Integer, Long> integerCountMap =
        Arrays.stream(array)
            .boxed()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
    return Arrays
        .stream(array)
        .boxed()
        .sorted(Comparator.<Integer, Long>comparing(integerCountMap::get)
            .reversed()
            .thenComparing(Comparator.naturalOrder()))
        .mapToInt(Integer::intValue)
        .toArray();
  }
}

Map으로 (int, count) 데이터를 만들고, EntrySet list를 통해 value(count) 순으로 정렬 (count가 같다면 key 오름차순), 그리고 result list에 하나씩 담아 배열로 변환하여 리턴

근데 Best Practice를 보니까 Stream으로 신박하게 해결했다. 참고하면 좋을듯?

Collectors.groupingBy(), Function.identity(), Collectors.counting(), Comparator.<Integer, long>comparing() 메소드들은 처음 보는 메소드들인데 알아두면 Stream에서 요긴하게 쓸 수 있을듯

 

 

 

파이썬 코드

def solve(arr):
    int_count_map = {}
    for integer in arr:
        int_count_map.setdefault(integer, 0)
        int_count_map[integer] = int_count_map.get(integer) + 1

    sorted_by_value_dict = sorted(int_count_map.items(), key=lambda x: x[1], reverse=True)

    result = []
    for items in sorted_by_value_dict:
        key = items[0]
        value = items[1]
        for i in range(value):
            result.append(key)

    return result


def solve_best_practice(arr):
    return sorted(sorted(arr), key=lambda n: arr.count(n), reverse=True)

파이썬으로도 풀어보자 해서 억지로 풀긴했다. 근데 BestPractice보니까 헛웃음이 나더라

자바에서 푼 것 처럼 dict에 (int, count)로 담고, count 내림차순으로 정렬했다.

근데 BestPractice보니까 그냥 개 쉽게 풀어버림.. 당황스럽네

 

1. 문제

www.codewars.com/kata/55b3425df71c1201a800009c

 

Codewars: Achieve mastery through challenge

Codewars is where developers achieve code mastery through challenge. Train on kata in the dojo and reach your highest potential.

www.codewars.com

문제 설명

You are the "computer expert" of a local Athletic Association (C.A.A.). Many teams of runners come to compete. Each time you get a string of all race results of every team who has run. For example here is a string showing the individual results of a team of 5 runners:

"01|15|59, 1|47|6, 01|17|20, 1|32|34, 2|3|17"

Each part of the string is of the form:h|m|swhere h, m, s (h for hour, m for minutes, s for seconds) are positive or null integer (represented as strings) with one or two digits. There are no traps in this format.

To compare the results of the teams you are asked for giving three statistics;range, average and median.

Range: difference between the lowest and highest values. In {4, 6, 9, 3, 7} the lowest value is 3, and the highest is 9, so the range is 9 − 3 = 6.

Mean or Average: To calculate mean, add together all of the numbers in a set and then divide the sum by the total count of numbers.

Median: In statistics, the median is the number separating the higher half of a data sample from the lower half. The median of a finite list of numbers can be found by arranging all the observations from lowest value to highest value and picking the middle one (e.g., the median of {3, 3, 5, 9, 11} is 5) when there is an odd number of observations. If there is an even number of observations, then there is no single middle value; the median is then defined to be the mean of the two middle values (the median of {3, 5, 6, 9} is (5 + 6) / 2 = 5.5).

the form:

"Range: hh|mm|ss Average: hh|mm|ss Median: hh|mm|ss"

where hh, mm, ss are integers (represented by strings) witheach 2 digits.

제한 사항

Remarks:

  1. if a result in seconds is ab.xy... it will be giventruncatedas ab.

  2. if the given string is "" you will return ""

입출력 예

input - "01|15|59, 1|47|6, 01|17|20, 1|32|34, 2|3|17"

result - "Range: hh|mm|ss Average: hh|mm|ss Median: hh|mm|ss"


2. 어떻게 풀까?

  1. input으로 들어온 time 문자열을 쪼갠다.
  2. 각 time 문자열을 시간, 분, 초를 계산해서, 혹은 LocalTime을 이용해서 List에 저장 후 정렬한다.
    • 전에 Time 패키지 공부 한 것을 써먹기 위해 LocalTime을 사용해서 해결
  3. List를 통해 Range, Average, Median을 구하고 String으로 표현한다.

3. 코드

테스트 코드

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;

import org.junit.Test;

public class StatTest {

  @Test
  public void testWhenInputIsEmptyString() {
    String expected = "";
    String input = "";
    assertThat(Stat.stat(input), is(expected));
  }

  @Test
  public void testWhenInputSizeIs1() {
    String expected = "Range: 00|00|00 Average: 00|15|00 Median: 00|15|00";
    String input = "0|15|0";
    assertThat(Stat.stat(input), is(expected));
  }

  @Test
  public void testWhenInputSizeIsOdd() {
    String expected = "Range: 01|01|18 Average: 01|38|05 Median: 01|32|34";
    String input = "01|15|59, 1|47|16, 01|17|20, 1|32|34, 2|17|17";
    assertThat(Stat.stat(input), is(expected));
  }

  @Test
  public void testWhenInputSizeIsEven() {
    String expected = "Range: 00|31|17 Average: 02|25|24 Median: 02|19|40";
    String input = "02|15|59, 2|47|16, 02|17|20, 2|32|34, 2|17|17, 2|22|00";
    assertThat(Stat.stat(input), is(expected));
  }

}

실제 코드


import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Stat {

  public static String stat(String strg) {
    if (strg.isEmpty()) {
      return "";
    }
    List<LocalTime> list = getSortedTimeList(strg);

    LocalTime range = getRange(list);
    LocalTime average = getAverage(list);
    LocalTime median = getMedian(list);

    return getResultString(range, average, median);
  }

  private static List<LocalTime> getSortedTimeList(String strg) {
    List<LocalTime> list = new ArrayList<>();
    String[] split = strg.split(", ");
    for (int i = 0; i < split.length; i++) {
      LocalTime temp = LocalTime.parse(split[i], DateTimeFormatter.ofPattern("H|mm|s"));
      list.add(temp);
    }
    list.sort(Comparator.naturalOrder());
    return list;
  }

  private static LocalTime getRange(List<LocalTime> list) {
    LocalTime min = list.get(0);
    LocalTime max = list.get(list.size() - 1);
    long seconds = min.until(max, ChronoUnit.SECONDS);

    return LocalTime.ofSecondOfDay(seconds);
  }

  private static LocalTime getAverage(List<LocalTime> list) {
    long seconds = 0;
    for (int i = 0; i < list.size(); i++) {
      seconds += list.get(i).toSecondOfDay();
    }
    return LocalTime.ofSecondOfDay(seconds / list.size());
  }

  private static LocalTime getMedian(List<LocalTime> list) {
    long seconds = 0;
    if (list.size() % 2 == 1) {
      seconds = list.get(list.size() / 2).toSecondOfDay();
    } else {
      seconds += list.get(list.size() / 2).toSecondOfDay();
      seconds += list.get(list.size() / 2 - 1).toSecondOfDay();
      seconds /= 2;
    }
    return LocalTime.ofSecondOfDay(seconds);
  }

  private static String getResultString(LocalTime range, LocalTime average, LocalTime median) {
    return String.format("Range: %s Average: %s Median: %s",
        range.format(DateTimeFormatter.ofPattern("HH|mm|ss")),
        average.format(DateTimeFormatter.ofPattern("HH|mm|ss")),
        median.format(DateTimeFormatter.ofPattern("HH|mm|ss")));
  }
}
  • getSortedTimeList() 에서 input 문자열을 ", "로 쪼갠 뒤 List에 저장 후 정렬했다.
  • getRange(), getAverage(), getMedian()에서 List에 저장한 LocalTime들을 계산했다.

4. 느낀 점

  • 문제를 처음 보고 '|'의 index를 통해 시, 분, 초를 구하고 그것들을 초로 바꾸어 List에 저장해서 정렬 후, range/average/medain을 계산하려 했는데, 전에 공부했던 Time 패키지의 LocalTime을 이용하면 쉽게 해결 할 수 있겠다고 생각해서 사용해 보았다.
  • List<LocalTime>을 정렬하면 정렬이 안될줄 알았는데 아주 잘 되서 신기했다.
  • LocalTime을 이용할 때, 전에 포스팅한 Time 패키지를 참고했다. 정리 해놓길 잘했다는 생각이 든다.
 

[Java] Time 패키지 (LocalTime, LocalDate, LocalDateTime)

1. Date, Calendar 클래스 이전 포스팅을 보자 jino-dev-diary.tistory.com/entry/Java-Date-Calendar-%ED%81%B4%EB%9E%98%EC%8A%A4?category=941695 요약하자면, Date 클래스와 Calendar 클래스는 문제점이 많아..

jino-dev-diary.tistory.com

 

1. 문제

www.codewars.com/kata/56a32dd6e4f4748cc3000006

 

Codewars: Achieve mastery through challenge

Codewars is where developers achieve code mastery through challenge. Train on kata in the dojo and reach your highest potential.

www.codewars.com

문제 설명

data and data1 are two strings with rainfall records of a few cities for months from January to December. The records of towns are separated by\n. The name of each town is followed by:

data and towns can be seen in "Your Test Cases:".

Task:

  • function:mean(town, strng)should return the average of rainfall for the citytown and the strng data ordata1(In R and Julia this function is calledavg).
  • function:variance(town, strng) should return the variance of rainfall for the city town and the strng data or data1.

제한 사항

  • if functions mean or variance have as parameter town a city which has no records return -1 or -1.0 (depending on the language)

  • Don't truncate or round: the tests will pass if abs(your\_result - test\_result) <= 1e-2 or abs((your\_result - test\_result) / test\_result) <= 1e-6 depending on the language.

  • Shell tests only variance

  • A ref:http://www.mathsisfun.com/data/standard-deviation.html

  • data and data1(can be name d0 and d1 depending on the language; see "Sample Tests:") are adapted from:http://www.worldclimate.com

입출력 예

mean("London", data), 51.19(9999999999996)

variance("London", data), 57.42(833333333374)


2. 어떻게 풀까?

  • 우선 data\n으로 도시들이 구분되어 있고, 도시와 월별 rainfall은 :로 구분되어있다고 하니 data를 통해 각 도시와 rainfall을 split 하는 게 중요해 보인다.
  • 그 이후에는 각 rainfall들을 더해서 12로 나누어 평균값(mean)을 구하고, mean을 이용해서 variance도 구하면 되겠다.
  • mean은 평균값인게 이해가 갔는데 variance는 뭔지 몰랐다. 근데 참고 링크를 보니까 딱 알겠더라.
  • 제한 사항을 보니 입력의 towndata or data1에 존재하지 않으면 -1을 리턴하라고 한다.
    • 처음에는 입력으로 주어진 town에 대한 결과값만 리턴하면 되겠다. 생각했는데, 이 제한사항 때문에 Map을 이용해야겠다고 생각했다.

3. 코드

테스트 코드

package codeWars.kyu6.rainFall_20201117;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;

import org.junit.Test;

public class RainfallTest {

  public static String data =
      "Rome:Jan 81.2,Feb 63.2,Mar 70.3,Apr 55.7,May 53.0,Jun 36.4,Jul 17.5,Aug 27.5,Sep 60.9,Oct 117.7,Nov 111.0,Dec 97.9"
          +
          "\n" +
          "London:Jan 48.0,Feb 38.9,Mar 39.9,Apr 42.2,May 47.3,Jun 52.1,Jul 59.5,Aug 57.2,Sep 55.4,Oct 62.0,Nov 59.0,Dec 52.9"
          +
          "\n" +
          "Paris:Jan 182.3,Feb 120.6,Mar 158.1,Apr 204.9,May 323.1,Jun 300.5,Jul 236.8,Aug 192.9,Sep 66.3,Oct 63.3,Nov 83.2,Dec 154.7"
          +
          "\n" +
          "NY:Jan 108.7,Feb 101.8,Mar 131.9,Apr 93.5,May 98.8,Jun 93.6,Jul 102.2,Aug 131.8,Sep 92.0,Oct 82.3,Nov 107.8,Dec 94.2"
          +
          "\n" +
          "Vancouver:Jan 145.7,Feb 121.4,Mar 102.3,Apr 69.2,May 55.8,Jun 47.1,Jul 31.3,Aug 37.0,Sep 59.6,Oct 116.3,Nov 154.6,Dec 171.5"
          +
          "\n" +
          "Sydney:Jan 103.4,Feb 111.0,Mar 131.3,Apr 129.7,May 123.0,Jun 129.2,Jul 102.8,Aug 80.3,Sep 69.3,Oct 82.6,Nov 81.4,Dec 78.2"
          +
          "\n" +
          "Bangkok:Jan 10.6,Feb 28.2,Mar 30.7,Apr 71.8,May 189.4,Jun 151.7,Jul 158.2,Aug 187.0,Sep 319.9,Oct 230.8,Nov 57.3,Dec 9.4"
          +
          "\n" +
          "Tokyo:Jan 49.9,Feb 71.5,Mar 106.4,Apr 129.2,May 144.0,Jun 176.0,Jul 135.6,Aug 148.5,Sep 216.4,Oct 194.1,Nov 95.6,Dec 54.4"
          +
          "\n" +
          "Beijing:Jan 3.9,Feb 4.7,Mar 8.2,Apr 18.4,May 33.0,Jun 78.1,Jul 224.3,Aug 170.0,Sep 58.4,Oct 18.0,Nov 9.3,Dec 2.7"
          +
          "\n" +
          "Lima:Jan 1.2,Feb 0.9,Mar 0.7,Apr 0.4,May 0.6,Jun 1.8,Jul 4.4,Aug 3.1,Sep 3.3,Oct 1.7,Nov 0.5,Dec 0.7";

  public static String data1 =
      "Rome:Jan 90.2,Feb 73.2,Mar 80.3,Apr 55.7,May 53.0,Jun 36.4,Jul 17.5,Aug 27.5,Sep 60.9,Oct 147.7,Nov 121.0,Dec 97.9"
          +
          "\n" +
          "London:Jan 58.0,Feb 38.9,Mar 49.9,Apr 42.2,May 67.3,Jun 52.1,Jul 59.5,Aug 77.2,Sep 55.4,Oct 62.0,Nov 69.0,Dec 52.9"
          +
          "\n" +
          "Paris:Jan 182.3,Feb 120.6,Mar 188.1,Apr 204.9,May 323.1,Jun 350.5,Jul 336.8,Aug 192.9,Sep 66.3,Oct 63.3,Nov 83.2,Dec 154.7"
          +
          "\n" +
          "NY:Jan 128.7,Feb 121.8,Mar 151.9,Apr 93.5,May 98.8,Jun 93.6,Jul 142.2,Aug 131.8,Sep 92.0,Oct 82.3,Nov 107.8,Dec 94.2"
          +
          "\n" +
          "Vancouver:Jan 155.7,Feb 121.4,Mar 132.3,Apr 69.2,May 85.8,Jun 47.1,Jul 31.3,Aug 37.0,Sep 69.6,Oct 116.3,Nov 154.6,Dec 171.5"
          +
          "\n" +
          "Sydney:Jan 123.4,Feb 111.0,Mar 151.3,Apr 129.7,May 123.0,Jun 159.2,Jul 102.8,Aug 90.3,Sep 69.3,Oct 82.6,Nov 81.4,Dec 78.2"
          +
          "\n" +
          "Bangkok:Jan 20.6,Feb 28.2,Mar 40.7,Apr 81.8,May 189.4,Jun 151.7,Jul 198.2,Aug 197.0,Sep 319.9,Oct 230.8,Nov 57.3,Dec 9.4"
          +
          "\n" +
          "Tokyo:Jan 59.9,Feb 81.5,Mar 106.4,Apr 139.2,May 144.0,Jun 186.0,Jul 155.6,Aug 148.5,Sep 216.4,Oct 194.1,Nov 95.6,Dec 54.4"
          +
          "\n" +
          "Beijing:Jan 13.9,Feb 14.7,Mar 18.2,Apr 18.4,May 43.0,Jun 88.1,Jul 224.3,Aug 170.0,Sep 58.4,Oct 38.0,Nov 19.3,Dec 2.7"
          +
          "\n" +
          "Lima:Jan 11.2,Feb 10.9,Mar 10.7,Apr 10.4,May 10.6,Jun 11.8,Jul 14.4,Aug 13.1,Sep 23.3,Oct 1.7,Nov 0.5,Dec 10.7";

  @Test
  public void meanTest() {
    assertThat(Rainfall.mean("London", data), is(51.199999999999996));
    assertThat(Rainfall.mean("Beijing", data), is(52.416666666666664));
  }

  @Test
  public void varianceTest() {
    assertThat(Rainfall.variance("London", data), is(57.42833333333374));
    assertThat(Rainfall.variance("Beijing", data), is(4808.37138888889));
  }

  @Test
  public void whenDataHasNotTownTest() {
    assertThat(Rainfall.mean("Lon", data), is(-1.0));
    assertThat(Rainfall.variance("Lon", data), is(-1.0));
  }
}

실제 코드

package codeWars.kyu6.rainFall_20201117;

import java.util.HashMap;
import java.util.Map;

public class Rainfall {

  public static double mean(String town, String strng) {
    Map<String, Double> townRainfallMeanMap = buildTownRainfallMeanMap(strng);

    return townRainfallMeanMap.getOrDefault(town, -1.0);
  }

  private static Map<String, Double> buildTownRainfallMeanMap(String strng) {
    Map<String, Double> townRainfallMeanMap = new HashMap<>();
    String[] townAndMonthRainfalls = strng.split("\n");

    for (String townAndMonthRainfall : townAndMonthRainfalls) {
      String targetTown = townAndMonthRainfall.split(":")[0];
      String[] monthRainfalls = townAndMonthRainfall.split(":")[1].split(",");
      double sumOfRecords = 0;

      for (String monthRainfall : monthRainfalls) {
        sumOfRecords += Double.parseDouble(monthRainfall.split(" ")[1]);
      }
      townRainfallMeanMap.put(targetTown, sumOfRecords / 12);
    }

    return townRainfallMeanMap;
  }

  public static double variance(String town, String strng) {
    Map<String, Double> townRainfallVarianceMap = buildTownRainfallVarianceMap(town, strng);

    return townRainfallVarianceMap.getOrDefault(town, -1.0);
  }

  private static Map<String, Double> buildTownRainfallVarianceMap(String town, String strng) {
    Map<String, Double> townRainfallVarianceMap = new HashMap<>();
    double mean = mean(town, strng);
    String[] townAndMonthRainfalls = strng.split("\n");

    for (String townAndMonthRainfall : townAndMonthRainfalls) {
      String targetTown = townAndMonthRainfall.split(":")[0];
      String[] monthRainfalls = townAndMonthRainfall.split(":")[1].split(",");
      double sumOfRecords = 0;

      for (String monthRainfall : monthRainfalls) {
        double rainfall = Double.parseDouble(monthRainfall.split(" ")[1]);
        sumOfRecords += Math.pow((rainfall - mean), 2);
      }
      townRainfallVarianceMap.put(targetTown, sumOfRecords / 12);
    }

    return townRainfallVarianceMap;
  }
}

mean()variance() 둘 다 비슷하게 구현했다. data로 주어진 strng\n으로 쪼개 town별로 분리하고, 분리한 town String 배열에서 :으로 쪼개 도시와 월별 rainfall로 분리했다. 그리고 rainfall끼리 전부 더해서 12로 나누어준 값을 map에 저장했다. varinace()에서는 mean()을 이용하여 variance를 구했다.


4. 느낀점

  • 어렵지 않은 문제였다. String을 어떻게 잘 쪼개냐가 관건인 것 같다.

1. 문제

https://www.codewars.com/kata/557f6437bf8dcdd135000010

 

Codewars: Achieve mastery through challenge

Codewars is where developers achieve code mastery through challenge. Train on kata in the dojo and reach your highest potential.

www.codewars.com

 

문제 설명

n mathematics, the factorial of integer n is written as n!. It is equal to the product of n and every integer preceding it. For example: 5! = 1 x 2 x 3 x 4 x 5 = 120

Your mission is simple: write a function that takes an integer n and returns the value of n!.

You are guaranteed an integer argument. For any values outside the non-negative range, return null, nil or None (return an empty string "" in C and C++). For non-negative numbers a full length number is expected for example, return 25! = "15511210043330985984000000" as a string.

For more on factorials, see http://en.wikipedia.org/wiki/Factorial

NOTES:

  • The use of BigInteger or BigNumber functions has been disabled, this requires a complex solution

  • I have removed the use of require in the javascript language.

입출력 예

1! 👉 "1"

5! 👉 "120"

9! 👉 "362880"

15! 👉 "1307674368000"

25! 👉 "15511210043330985984000000"

 


2. 어떻게 풀까?

 

간단하게 팩토리얼 구하는 함수를 구현해라는 문제인데, NOTE를 보면 BigInteger나 BigNumber를 쓰지 말라고 한다. 이러면 방법을 잘 모르겠다. 처음에는 bit별로 나누어서 list에 저장하고 bit끼리 더하면 되겠다 생각했는데 생각처럼 잘 되지 않았다. 그래서 결국 답을 봤다.

근데 답보니까 다 BigInteger를 썼는데 ㅋㅋ.. 그중에 안쓴 답이 있어서 며칠 동안 다시 풀면서 직접 코드를 짜 봤다.

 


3. 코드

 

답을 보고 내가 리팩터링 한 코드

 

테스트 코드

package doNotSolve.codewars.kyu4.largeFactorials_20201029;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;

import org.junit.Test;

public class KataTest {

  @Test
  public void test1() {
    assertThat(Kata.factorial(1), is("1"));
  }

  @Test
  public void test2() {
    assertThat(Kata.factorial(2), is("2"));
  }

  @Test
  public void test3() {
    assertThat(Kata.factorial(5), is("120"));
  }

  @Test
  public void test4() {
    assertThat(Kata.factorial(15), is("1307674368000"));
  }

  @Test
  public void test5() {
    assertThat(Kata.factorial(25), is("15511210043330985984000000"));
  }
}

 

실제 코드

package doNotSolve.codewars.kyu4.largeFactorials_20201029;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class KataAgain {

  static int carry;

  public static String factorials(int n) {
    List<Integer> digits = new ArrayList<>();
    int value = n;
    saveDigits(digits, value);

    for (value = n - 1; value > 1; value--) {
      List<Integer> targetDigits = new ArrayList<>();
      carry = 0;
      saveDigits(digits, targetDigits, value);

      saveDigits(targetDigits, carry);
      digits = targetDigits;
    }

    return getResultString(digits);
  }

  private static void saveDigits(List<Integer> digits, List<Integer> targetDigits, int value) {
    for (Integer digit : digits) {
      int product = value * digit + carry;
      targetDigits.add(product % 10);
      carry = product / 10;
    }
  }

  private static void saveDigits(List<Integer> digits, int value) {
    if (value == 0) {
      return;
    }
    while (value > 0) {
      digits.add(value % 10);
      value /= 10;
    }
  }

  private static String getResultString(List<Integer> digits) {
    String reversedDigits = digits.stream().map(String::valueOf).collect(Collectors.joining());
    return new StringBuilder(reversedDigits).reverse().toString();
  }
}

4. 느낀 점

memoization 방법으로 팩토리얼을 구현하면 타입을 long으로 해도 20! 에서 오버플로우가 발생한다. 따라서 큰 값의 팩토리얼을 구하려면 BigInteger을 사용해왔는데 새로운 방법을 알게 되었다. 팩토리얼 외에도 피보나치수열도 이 방법으로 할 수 있지 않을까? 생각이 들었다.

+ Recent posts