ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준 Node.js] 4673번 셀프넘버 (함수)
    To infinity/Coding Practice 2021. 6. 26. 08:16

    2021.06.25-26

    Question

     

     

    4673번: 셀프 넘버

    셀프 넘버는 1949년 인도 수학자 D.R. Kaprekar가 이름 붙였다. 양의 정수 n에 대해서 d(n)을 n과 n의 각 자리수를 더하는 함수라고 정의하자. 예를 들어, d(75) = 75+7+5 = 87이다. 양의 정수 n이 주어졌을 때,

    www.acmicpc.net


    Answer code

    function NotSelfnumber(N){
        
        //숫자 하나를 더하는 것에 대한 함수
        let sum = N
    
        while(true){   
            if(N == 0) break;
            sum += N%10
            N = parseInt(N/10)
        }
        return sum; //무엇을 return할지 꼭 써주기
    }
    
    function selfnumber(N){
        let selfnum = []
        let result = []
    
        for(let i=1; i <= N; i++){
            let index = NotSelfnumber(i); 
            
            if(index <= N){
                selfnum[index] = true;
            }
        }
    
        for(let i=1; i<= N; i++){
            if(!selfnum[i]) result.push(i);
        }
        console.log(result.join('\n'));
    }
    
    selfnumber(10000);

     

    [참고] 

    // 결과값을 도출하는 함수
    for(let i=1; i<= N; i++){
            if(!selfnum[i]) result.push(i);
        }
        
    
    // 위 코드는 아래와같이 쓸 수도 있다.
    for(let j=1; j<=10000; j++){
            if(selfnum[j] = j) continue //되는구나
            else {result.push(j);}
        }

    How to solve?

     

    Brain Storming

     (1) 생성자를 구하는 함수를 하나 만들고

     (2) 셀프넘버를 구하는 함수를 하나 만든다.

    총 2개의 함수를 만든다.

     

     

     

    (1) 생성자 구하는 함수 만들기

    function NotSelfnumber(N){
        let sum = N //(1)(2)
    
        while(true){   
            if(N == 0) break;  //(4) 1의자리수를 계속 없애기 때문에 결국 1의자리 수는 0이 된다.
            sum += N%10  //(2) 1의자리수 더하기
            N = parseInt(N/10)  //(3) 1의자리수 없애기
        }
        return sum; // 무엇을 return 할 것인지 꼭 써주기
    }

     

    해당 함수는 '생성자 값 하나'만 구하는 함수다.

    즉, 내가 N에 9645을 넣으면 9645 + 5 + 4 + 6 +9 라는 계산을 통해 9669의 값을 반환한다.

    해당 수열은 자기자신(9645)과 각 자리숫자의 합(1000의 자리, 100의 자리, 10의 자리, 1의 자리)로 이루어져 있으므로

    함수를 짤 때 자기자신 + 각 자리숫자의 합으로 구분해서 로직을 짠다.

     

    해당 로직은 N자리수로 이루어진 숫자를 1의 자리를 더하고 1의자리를 제거하여 결과적으로 모든 자리수를 다 더할 수 있도록 만든 로직이다.

     

    간과한 사실 (Mistake)

    (1) 예시만 보고 모든 경우의수에대한 로직을 못짰다. 즉, 10000까지 숫자가 들어오기 때문에 최대 자리수까지 더해주는 로직을 만들어야 하는데 예시만 보고선 두자리수만 더하는 식 (N+parseInt(N/10)+(N%10)) 을 만들었다.

     

    (2) 10000까지의 생성자를 모두 구하는 함수를 만드려했다. 즉, 한 함수에 너무 많은 정보를 담으려 했다.

    10000까지 구하지 않더라도 해당 로직이 돌아가는 가장 작은 단위의 규칙성을 파악하면 함수를 만들 때 크게 size up 시키지 않아도 된다.

     

    위의 소스코드에서는 생성자 숫자 1개가 어떤 로직으로 수열이 돌아가는지를 파악해서 해당 값 1개를 구하는 함수를 짰다. 하지만 나는 10000까지 더해지는 것이 하나의 덩어리로 생각해서 볼륨을 너무 크게 생각했다.

    그래서 위 Sketon의 (1)(2)번 코드를 한 함수 안에 다 담으려했다.

     

     

     

    (2) 셀프넘버 함수 만들기

    function selfnumber(N){
        let selfnum = []
        let result = []
    
        for(let i=1; i <= N; i++){
            let index = NotSelfnumber(i); //index라는 새로운 변수에 생성자 갚을 넣는다
            
            if(index <= N){  // 생성자 값 중에 N이 넘는 값이 있을 수 있으니 범위를 제한하고
                selfnum[index] = true;  //selfnum이라는 새로운 배열의 각 인덱스 자리에 생성자 값을 넣는다.
            }
        }
    
        for(let i=1; i<= N; i++){
            if(!selfnum[i]) result.push(i);
        }
        console.log(result.join('\n'));
    }
    
    selfnumber(100);

     

    내가 생각했던 로직은 생성자함수로 만들어진 수열 값을 배열에 넣고(NotSelfnumber),

    해당 배열을 오름차순으로 정렬한 후에(sort)

    반복문으로 인덱스를 순차적으로 돌면서 NotSelfnumber의 인덱스를 비교해서 불일치하는 인덱스 값을 새로운 배열에 넣으면 selfnumber를 찾을 수 있을 것이라 생각했다.

    하지만 NotSelfnumber의 배열의 인덱스는 각 요소와 매치되도록 들어가 있지가 않다.

     

    즉, 내가 생각했던 로직을 실현시키려면 NotSelfnumber = [undefined, undefined, 2, undefined, 4, ... ] 이런 식으로 이뤄져있어야 했던 것이다. 

     

    인덱스와 매치되는지 확인하는 로직은 인덱스 자체를 돌면서 인덱스자리에 해당 값을 넣는거지만

    위에 적은 로직은 배열자체를 돌면서 인덱스자리에 배열값을 넣어줘야하기때문이다.

    (해당 아이디어는 2577번 숫자의 개수 문제에서 착안했다.)

     

    정리하면

    index = [1, 2, 3, 4, 5, 6, 7, 8, ..]

    NotSelfnumber = [2, 4, 6, 8, ...]

    selfnumber = [1, 3, 5, 7, ...]

     

    이러한 배열이 있다면 index - NotSelfnumber = selfnumber가 되기 때문에 어떻게하면 그 차이값을 구할 수 있을지 여러가지 생각을 해봤다. 그리고 최대한 JS메서드를 이용해보려했다.

     

    //셀프넘버 구하는 식을 for문 돌리기
    function notselfnumber(){
    	
        for(let i = 1; i <= 10000; i++){
        	let num = new Set()
            num.add(selfnumber(i));
        }
        
        Set.prototype.difference = function (set) {
            const result = new Set(this);
    
            for(const value of set) {
                result.delete(value);
            }
        return result;
        }
    }

     

    그래서 Set 메서드를 사용해서 차집합을 이용해보려고도 했고, forEach문을 써보려고도 했다.

    하지만 가장 근본적인 부분, 어떻게 변수 index랑 NotSelfnumber의 인덱스랑 일치시킬건데?가 해결되지 않았다.

     

    물론, for문으로 index를 돌려 새로운 배열을 만들어내는 방법도 있겠지만 그건 너무 for문을 많이 사용하게 되고 성능이 좋을 것 같지 않단 생각이 들었다.

     

     

    그러다가 인풋이 없으니 아웃풋이 없다는 생각이 들어 (뭐 아는게 있어야 응용을하지) 다른 사람들은 어떻게 로직을 짜는지, 내가 봤을 때 '오 좋다'라고 여겨지는 로직들을 차용하기로 한다. 그래야지 어떤 식으로 코드를 짜면 좋을지도 알게되고 코드를 짜는 시야도 넓어질 것이란 생각이 들었다.

     

    그래서 BlockMask님의 코드로직을 많이 참고했다. 언어는 다르지만 결국 문제를 해결하는 기본로직은 동일할테니까.

     

        let selfnum = []
        let result = []
    
        for(let i=1; i <= N; i++){
            let index = NotSelfnumber(i); //index라는 새로운 변수에 생성자 값을 넣는다
            
            if(index <= N){  // 생성자 값 중에 N이 넘는 값이 있을 수 있으니 범위를 제한하고
                selfnum[index] = true;  //selfnum이라는 새로운 배열의 각 인덱스 자리에 생성자 값을 넣는다.
            }
        }

     

    위에서 이야기했던 생성자함수의 값과 동일한 인덱스에 넣는 방법에 대한 아이디어가 여기 있었다.

    생성자함수 값을 넣고 그 배열을 다시 돌리려하지말고, 넣으면서 값을 boolean값인 true로 바꿔주는 것이다.

     

    그렇다면 최종적으로 만들어진 배열은 [undefiend, true, undefined, true, ...] 인 희소배열로 생성 될 것이며 값은  true or false 두가지 일 것이다.

     

        for(let i=1; i<= N; i++){
            if(!selfnum[i]) result.push(i);
        }
        console.log(result.join('\n'));

     

    그럼 해당 배열을 다시 for문을 돌려서 인덱스 값이 false인 인덱스만 새로운 배열에 넣으면 그것이 바로 셀프넘버가 된다. 아하.

     

    그렇게 또한번 깨달음을 얻는다.

    문제를 풀수록 느끼는건 앞에서 사용했던 로직을 뒤에서 응용해서 사용할 수 있게되는 것 같다.

     

    근데 또 궁금한건

    JS는 함수형 프로그래밍이어서 반복문이나 변수사용을 지양하고 JS에서 제공하는 메서드를 사용하는게 훨씬 좋다고 책에는 써있는데 내가 찾아보는 답들은 주로 for문을 많이 사용하는 것 같다. 왤까. 

     

     


    Reference

     

     

    [백준 4673] 셀프 넘버

    안녕하세요. BlockDMask 입니다. 추석날 2번째 문제 입니다. 왔다 갔다 차안에서 문제를 풀고 있습니다. 길이 많이 막히네요;; 170818 문제 빼먹음 -> 171004 완료 0. 제목 백준 4673 셀프 넘버 BOJ 4673 self.

    blockdmask.tistory.com

     

Designed by Tistory.