티스토리 뷰

2. 셀프 넘버

https://www.acmicpc.net/problem/4673

문제

셀프 넘버

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

양의 정수 n이 주어졌을 때, 이 수를 시작해서 n, d(n), d(d(n)), d(d(d(n))), ...과 같은 무한 수열을 만들 수 있다.

예를 들어, 33으로 시작한다면 다음 수는 33 + 3 + 3 = 39이고, 그 다음 수는 39 + 3 + 9 = 51, 다음 수는 51 + 5 + 1 = 57이다. 이런식으로 다음과 같은 수열을 만들 수 있다.

33, 39, 51, 57, 69, 84, 96, 111, 114, 120, 123, 129, 141, ...

n을 d(n)의 생성자라고 한다. 위의 수열에서 33은 39의 생성자이고, 39는 51의 생성자, 51은 57의 생성자이다. 생성자가 한 개보다 많은 경우도 있다. 예를 들어, 101은 생성자가 2개(91과 100) 있다.

생성자가 없는 숫자를 셀프 넘버라고 한다. 100보다 작은 셀프 넘버는 총 13개가 있다. 1, 3, 5, 7, 9, 20, 31, 42, 53, 64, 75, 86, 97

10000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 출력하는 프로그램을 작성하시오.

입력
입력은 없다.

출력
10,000보다 작거나 같은 셀프 넘버를 한 줄에 하나씩 증가하는 순서로 출력한다.

예제 입력 1

예제 출력 1
1
3
5
7
9
20
31
42
53
64
 |
 |       <-- a lot more numbers
 |
9903
9914
9925
9927
9938
9949
9960
9971
9982
9993

해답

function d(n) {
  let sum = n;

  while (n > 0) {
    sum += n % 10;
    n = Math.floor(n / 10);
  }

  return sum;
}

const selfNum = new Array(10000);
selfNum.fill(true);

for (let i = 1; i <= 10000; i++) {
  selfNum[d(i)] = false;

  if (selfNum[i]) {
    console.log(i);
  }
}

풀이

function d(n) (생성자를 구하는 함수)

function d(n) {
  let sum = n;

  while (n > 0) {
    sum += n % 10;
    n = Math.floor(n / 10);
  }

  return sum;
}

일정한 범위 내에서 생성자가 없는 숫자인 셀프 넘버를 구하기 위해서는 전체 범위에서 생성자를 빼주면 됩니다.

우선 생성자를 구하는 함수가 필요합니다.

sum (생성자)
let sum = n;

sum이라는 변수에 매개변수 양의 정수를 뜻하는 n을 할당합니다.

만약 n123인 경우 sum123이 됩니다.

while (생성자를 만드는 과정)
while (n > 0) {
  sum += n % 10;
  n = Math.floor(n / 10);
}
while (n > 0) {}

while문을 활용해 n > 0일 경우에만 진행을 하도록 합니다.

더하기 할당 (sum에 n의 나머지 값 더하기)
while (n > 0) {
  sum += n % 10;
}

더하기 할당을 통해 n 값을 할당한 변수 sumn의 10으로 나눈 나머지 즉 일의 자리 수를 더하여 줍니다.

예를 들어 숫자 123일 경우 3을 더하여 주는 것입니다.

Math.floor() (소수점 밑 버리기)
while (n > 0) {
  sum += n % 10;
  n = Math.floor(n / 10);
}

n10으로 나누면 소수점을 가진 실수가 되는데 Math.floor() 함수를 통해 정수화 해줍니다. 그리고 그 값을 n에 할당합니다.

예를 들어 123의 경우 n / 10을 하면 12.3이 되는데 Math.floor()을 통해 12가 됩니다.

위와 같은 과정을 while (n > 0) {}n0보다 큰 경우 반복하게 되어 두 자리 수든 세 자리 수든 모든 자리수를 더한 값을 구할 수 있습니다.

return (생성자 sum 반환하기)
function d(n) {
  let sum = n;

  while (n > 0) {
    sum += n % 10;
    n = Math.floor(n / 10);
  }

  return sum;
}

console.log(d(123)); // 129

return 명령문을 통해 함수 실행을 종료하고 sum 값을 반환합니다. 즉 n의 생성자 혹은 생성자들을 반환하게 되는 것입니다.

selfNum (셀프 넘버를 가지는 배열)

const selfNum = new Array(10000);
selfNum.fill(true);
Array() 생성자 (배열 생성하기)
const selfNum = new Array(10000);

Array()를 활용하여 10000개의 요소를 가지는 빈 배열을 생성하여 변수 selfNum에 할당합니다.

fill() (배열의 요소 모두 true)
selfNum.fill(true);

아직 셀프 넘버인지 생성자인지 추려내지 않았기 때문에 fill() 메서드를 활용하여 모두 true로 채웁니다.

for (selfNum 배열의 요소 추려내기)

for (let i = 1; i <= 10000; i++) {
  selfNum[d(i)] = false;

  if (selfNum[i]) {
    console.log(i);
  }
}
for (let i = 1; i <= 10000; i++) {}

selfNum 내의 요소들에 관한 반복문이므로 i의 범위를 1부터 10000까지로 합니다.

생성자인 경우 요소 false로
for (let i = 1; i <= 10000; i++) {
  selfNum[d(i)] = false;
}

d(i) 값과 일치하는 배열 selfNum의 요소인 경우 false로 할당합니다.

위의 과정을 통해 생성자인 요소는 false가 되는 것이고, 생성자가 아닌 셀프 넘버인 요소는 true 값을 가지게 됩니다.

if...else (selfNum의 요소가 true인 경우)
for (let i = 1; i <= 10000; i++) {
  selfNum[d(i)] = false;

  if (selfNum[i]) {
    console.log(i);
  }
}

if...else문에 의해 selfNum[i]true일 경우 셀프 넘버이므로 console.log() 메서드로 i 값을 출력해줍니다.

3. 한수

https://www.acmicpc.net/problem/1065

문제

한수

문제
어떤 양의 정수 X의 각 자리가 등차수열을 이룬다면, 그 수를 한수라고 한다. 등차수열은 연속된 두 개의 수의 차이가 일정한 수열을 말한다. N이 주어졌을 때, 1보다 크거나 같고, N보다 작거나 같은 한수의 개수를 출력하는 프로그램을 작성하시오.

입력
첫째 줄에 1,000보다 작거나 같은 자연수 N이 주어진다.

출력
첫째 줄에 1보다 크거나 같고, N보다 작거나 같은 한수의 개수를 출력한다.

예제 입력 1
110

예제 출력 1
99

예제 입력 2
1

예제 출력 2
1

예제 입력 3
210

예제 출력 3
105

예제 입력 4
1000

예제 출력 4
144

예제 입력 5
500

예제 출력 5
119

해답

const fs = require("fs");

const inputData = +fs.readFileSync(
  process.platform === "linux" ? "/dev/stdin" : "../../../../index.txt"
);

function arithmeticalSequence(n) {
  if (n < 100) {
    return true;
  } else if (n < 1000) {
    const number = n % 10;
    const tens = Math.floor(n / 10) % 10;
    const hundreds = Math.floor(n / 100);
    if (number - tens === tens - hundreds) {
      return true;
    } else {
      return false;
    }
  }
}

let count = 0;

for (let i = 1; i <= inputData; i++) {
  if (arithmeticalSequence(i)) {
    count++;
  }
}

console.log(count);

풀이

inputData (입력값)

readFileSync() (파일 읽어오기)
const fs = require("fs");

const inputData = fs.readFileSync(
  process.platform === "linux" ? "/dev/stdin" : "../../../../index.txt"
);

console.log(inputData); // <Buffer 31 30 30 30>

백준에서 추천하는 방식은 node.js에서 fs 모듈의 readFileSync()를 이용하는 것입니다.

process.platform (작동 시스템 구별)
process.platform === "linux" ? "/dev/stdin" : "../../../../index.txt";

process.platform"linux"인 경우 경로를 "/dev/stdin"으로 향하게 하고 그것이 아니면 사용자가 지정한 파일을 향하게 합니다.

+ (숫자형으로 변환)
const inputData = +fs.readFileSync(
  process.platform === "linux" ? "/dev/stdin" : "../../../../index.txt"
);

console.log(inputData); // 1000

Buffer 형식으로 출력된 inputData값을 숫자형으로 변환시킵니다.

arithmeticalSequence(n) (한수를 구하는 함수)

function arithmeticalSequence(n) {
  if (n < 100) {
    return true;
  } else if (n < 1000) {
    const number = n % 10;
    const tens = Math.floor(n / 10) % 10;
    const hundreds = Math.floor(n / 100);
    if (number - tens === tens - hundreds) {
      return true;
    } else {
      return false;
    }
  }
}

일정한 범위 내에서 한수의 개수를 출력을 해야합니다. 우선 특정한 수가 한수 즉 등차수열인지 아닌지를 가려내는 함수를 생성합니다.

if...else (범위마다 한수 추려내기)
if (n < 100) {
  return true;
} else if (n < 1000) {
  const number = n % 10;
  const tens = Math.floor(n / 10) % 10;
  const hundreds = Math.floor(n / 100);
  if (number - tens === tens - hundreds) {
    return true;
  } else {
    return false;
  }
}

1000 보다 작거나 같은 자연수에 중에 한수를 찾아내는 것이기 때문에 if...else문을 활용하여 범위마다 구분합니다.

n < 100 (n이 100보다 작을 때)
if (n < 100) {
  return true;
}

n 값이 100보다 작을 경우 각 자리의 수가 모두 등차수열을 이루기 때문에 true 값을 반환합니다.

n < 1000 (n 100 이상이면서 1000보다 작을 경우)
if (n < 100) {
  return true;
} else if (n < 1000) {
  const number = n % 10;
  const tens = Math.floor(n / 10) % 10;
  const hundreds = Math.floor(n / 100);
  if (number - tens === tens - hundreds) {
    return true;
  } else {
    return false;
  }
}

등차수열: 연속하는 두 항의 차이가 모두 일정한 수열

모두 true100미만의 경우를 제외하고 100이상 1000 미만일 경우 즉 세 자리 수부터는 각 자리수가 등차수열이 될 수 없는 경우가 존재합니다.

그 존재를 가려내기 위해서 number, tens, hundreds와 같이 각각 일의 자리 수, 십의 자리 수, 백의 자리 수를 의미하는 변수를 만들고,

등차수열의 특징에 따라 연속하는 두 항의 차이가 모두 일정해야하므로 number - tens === tens - hundreds 즉 일의 자리 수와 십의 자리 수의 차이가 십의 자리 수와 백의 자리 수의 차이와 같다면 true를 반환하고, 그것이 아니라면 false를 반환하게 합니다.

count (한수의 개수)

let count = 0;

한수의 개수를 의미하는 변수 count를 선언하고 0으로 초기화합니다.

for (inputData 값 이하의 수 한수 판단)

for (let i = 1; i <= inputData; i++) {
  if (arithmeticalSequence(i)) {
    count++;
  }
}

for문을 통해 i의 범위를 1부터 주어진 자연수인 inputData만큼으로 하여 이하의 식을 수행하게 합니다.

if...else (한수일 경우 count 값 증가)
if (arithmeticalSequence(i)) {
  count++;
}

위에서 정의한 한수를 판별하는 함수인 arithmeticalSequence(i)를 통해 i의 값이 한수이면 즉 반환하는 값이 true이면 증가 연산자를 통해 증가시킵니다.

console.log() (한수의 개수 출력하기)

console.log(count); // 144

console.log() 메서드를 활용해 한수의 개수를 의미하는 변수 count를 출력합니다.

댓글