정상혁정상혁

Andrian Walker라는 영국 개발자가 만든 라이브러리를 제가 수정해서 Github에 올렸습니다.

이 프로젝트에 대한 자세한 설명은 아래에 있습니다.

(github wiki에 적고 블로그에 붙여넣기를 하니 코드 블럭이 깨지는 등 편집이 쉽지가 않네요. 그래서 링크로 대신 합니다.)

위 글의 핵심 내용은 아래와 같습니다.

  • Java에는 여러 줄에 걸친 문자열을 선언하는 문법이 없어서 긴 문자열을 편집하는 작업이 불편합니다.

  • 이를 보완하는 방법을 찾던 중 Adrian Walker라는 개발자가 만든 Multiline-string 이라는 라이브러리를 발견했고, Eclipse에서도 쓸 수 있도록 코드를 수정해서 원저자의 허락을 받고 Github에 올렸습니다. (https://github.com/benelog/multiline)

  • 이 과정 중에 Annotation Processing과 ECJ(Eclipse compiler for Java)에 대해서 알게 된 것들을 정리했습니다.

  • 이 라이브러리와 비슷한 기능을 Lombok에 추가하거나, 안드로이드에서 annotation Processing을 활용할만한 방안을 더 연구해볼만 합니다

정상혁정상혁

계속 이어지는 인사이트 문제입니다.

image

문제

코딩 인터뷰 완전 분석』210쪽 17.3 변형 문제

자연수 n을 입력받고, n!의 계산 결과 중 마지막에 붙은 연속된 0의 개수와 연속된 0 바로 앞에 나오는 숫자를 구하라.

실행 예

input n: 15
output: 3 8

설명

15!은 1307674368000이므로, 마지막에 연속된 0은 3개이고, 바로 앞의 숫자는 8이다.

조건

  • n의 범위는 1 이상, 10000 이하입니다.

  • 테스트 입력은 다음과 같습니다.

20! = 2432902008176640000
30! = 265252859812191058636308480000000
40! = 815915283247897734345611269596115894272000000000
50! = 30414093201713378043612608166064768844377641568960512000000000000
100! = 93326215443944152681699238856266700490715968264381621468592963
8952175999932299156089414639761565182862536979208272237582511852
10916864000000000000000000000000
  • 프로그래밍 언어에서 제공하는 자릿수 제한 없는 곱셈을 이용하거나, 이런 형태의 곱셈 함수를 직접 구현해도 답을 얻을 수 있지만, 문제의 의도와는 다릅니다.

  • 정답 검토의 편의를 위해 블로그 포스팅에 2012!와 10000!의 결과를 남겨주세요.

  • (심화 문제) 연속된 0 앞에 나오는 여러 숫자를 구하는 것도 가능하니, 심심하신 분은 도전해보세요. ^^

풀이

먼저 입력값이 2012와 10000일 때의 결과는 아래와 같습니다.

input : 2012

output: 501 8


input : 10000

output: 2499 8

제가 푼 방식은 10은 5와 2의 배수라는 점을 이용했습니다. 곱해지는 숫자를 2와 5로만 소인수 분해하고 그 갯수를 각각 누적해서 구합니다. 즉 최종 팩토리안 값의 약수 중에서 2와 5가 몇 번 들어가 있는지를 계산하는 것이죠. 그리고 2,5가 아닌 나머지 약수는 구하고자하는 자릿수만큼만 잘라서 보관합니다.

예를 들면 5!에서 0의 갯수가 0이 아닌 마지막 숫자를 구한다면

5! = 1 * 2 * 3 * 4 *5 = 2^3 * 5 * 3

이이므로 2는 3번, 5는 1번 들어갑니다. 0의 갯수는 2,5가 짝이 맞는 갯수이므로 둘 중에 작은 숫자인 5의 약수의 갯수, 즉 1개입니다.

그리고 마지막 0이 아닌 숫자는 나머지 약수의 마지막 숫자인 3과 5와 짝이 맺어지지 못한 2의 배수를 곱해서 구합니다.

여기서는 약수인 2중 1개는 5와 짝이 맺어졌고, 나머지 2개가 남았으므로 2^2*3 = 12, 마지막 숫자만 남기면 2입니다.

테스트 코드에서는 이미 알려진 숫자를 검증했습니다.

public class FactorianAnalyzerTest {
    FactorianAnalyzer analyzer = new FactorianAnalyzer();

    @Test

    public void test5(){
        assertFactorianResult(5, 1, 2);
    }

    @Test
    public void test15(){
        assertFactorianResult(15, 3, 8);
    }

    @Test
    public void test20(){
        assertFactorianResult(20, 4, 4);
    }

    @Test
    public void test30(){
        assertFactorianResult(30, 7, 8);
    }

    @Test
    public void test40(){
        assertFactorianResult(40, 9, 2);
    }

    @Test
    public void test50(){
        assertFactorianResult(50, 12, 2);
    }

    @Test
    public void test100(){
        assertFactorianResult(100, 24, 4);
    }

그리고 꼭 마지막 1개의 숫자가 아닌 여러 숫자로 구할 수 있도록 확장했기 때문에 그것도 검증해보았습니다.

public class FactorianAnalyzerNonZeroTwoDigitsTest {

    FactorianAnalyzer analyzer = new FactorianAnalyzer();

    @Test
    public void test5(){
        assertFactorianResult(5, 2, 1, 12);
    }

    @Test
    public void test6(){
        assertFactorianResult(6, 2, 1, 72);
    }

    @Test
    public void test7(){
        assertFactorianResult(7, 3, 1, 504);
    }

    @Test
    public void test8(){
        assertFactorianResult(8, 2, 1, 32);
    }

    private void assertFactorianResult(int n,  int numOfLastNonZeroDigits, int expectedZeroCount, int expectedNonzeroDigit) {
        FactorianResult result = analyzer.countZero(n, numOfLastNonZeroDigits);
        assertThat(result.getZeroCount(), is(expectedZeroCount));
        assertThat(result.getNonZeroDigits(), is(expectedNonzeroDigit));
    }
}

전체 코드는 아래 주소에 올렸습니다.

정상혁정상혁

출판사 인사이트 블로그에 아래와 같은 퀴즈가 올라왔네요 ( 원문: http://www.insightbook.co.kr/post/3814 )

문제

배열 arr[]과 위치 s, t가 있을 때,arr[s], arr[s+1], … , arr[t-1]을 오른쪽으로 한 칸씩 이동하고,arr[t]는 arr[s]로 복사하는 것을 ’1만큼 오른쪽으로 회전시켰다’고 한다.

예를 들어 길이가 8인 배열에서 s=2, t=6이면 다음 그림처럼 바뀐다.

image

길이가 n인 배열의 위치는 0, 1, 2, … , n-1이다.

문제 :k를 인자로 받아서 k만큼 오른쪽으로 회전시키는 함수를 작성하라.단, 1만큼 오른쪽으로 이동시키는 과정을 k번 반복해서는 안 된다.

조건 1 : 작성하는 언어에는 제한이 없습니다. 조건 2 : 답안으로 작성하신 글 제목에는 ‘문제로 풀어보는 알고리즘 0.3 생각해보기 풀이’라는 문장이 들어가야 합니다. (저희 블로그에도 트랙백을 걸어주세요.)

(주의: 이 코딩 인터뷰는 인사이트 입사와는 무관합니다. ㅡㅁㅡ /)

풀이

밀린 일이 많아서 정신적인 여유가 없지만, 경품에 눈이 어두워서 급하게 풀어봤습니다

우선 테스트부터 먼저 작성하고

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

public class ArrayHandlerTest {
    ArrayHandler handler = new ArrayHandler();

    @Test
    public void testShift1() {
        int[] array = {1,2,3,4,5};
        handler.shift(array,0,3,1);
        assertThat(array, is(new int[]{4,1,2,3,5}));
    }

    @Test
    public void testShift2() {
        int[] array = {1,2,3,4,5};
        handler.shift(array,1,3,1);
        assertThat(array, is(new int[]{1,4,2,3,5}));
    }

    @Test
    public void testShift3() {
        int[] array = {1,2,3,4,5};
        handler.shift(array,1,2,1);
        assertThat(array, is(new int[]{1,3,2,4,5}));
    }
....

그리고 이를 통과시키는 실행코드입니다.

import java.util.Arrays;public class ArrayHandler {
    public void shift(int[] array, int s, int t, int k) {
        if (s > t) return;
        if (t >= array.length) return;

        int[] rangeToShift = Arrays.copyOfRange(array, s, t + 1);
        int rangeLength = t-s + 1; for(int i=0; i< rangeLength; i++) {
            int offset = (i+k) % rangeLength;
            array[s + offset] = rangeToShift[i];
        }
    }
}

대용량 배열일 때 메모리를 더 적게 쓰는 방법들도 고민해볼만도 하지만, 우선은 쉽게 문제를 푸는데 집중했습니다. t,s, k 같은 한글자 변수명은 별로 좋아하지 않지만, 문제에 있는 변수명이라서 그대로 살렸습니다.

전체 소스는 Github에 올렸습니다.

참고로 java.util.Collections.roate()메소드를 쓰면 위 문제는 아래 두 줄로 풀 수 있기도 합니다.

[source,java

List<Integer> range = list.subList(s, t+1);
Collections.rotate(range, k);

그런데 이렇게 하는게 이 문제의 의도는 아닐듯해서 참고구현 정도로 남겨 두었습니다. 처음 풀이에서도 Arrays.copyOfRange 메소드를 쓰기는 했지만 이 부분은 특별한 알고리즘에 없는 단순한 배열복사라서 활용했습니다.