• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

    • About
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

정보처리기사 실기 알고리즘 정리 02

30 Jul 2018

Reading time ~11 minutes

정보처리기사 실기 알고리즘 정리 02


임의의 정수를 입력하여 소수를 판별하시오(나누어 떨어지지 않을 때)

알고리즘의 이해

  • 소수 : 1보다 크며 1과 자기 자신만을 약수로 가지는 수
    • 4는 1,2,4를 약수로 가지므로 소수가 아님
    • 5는 1과 5만을 약수로 가지므로 소수
  • 어떤 수 n이 소수인지 판별하려면 n을 2부터 n보다 1작은 수(n-1)까지 차례대로 나누어 떨어지는지 검사하면 됨.
    • 나누어 떨어진다는 의미는 나머지가 0임을 뜻함
    • 5는 2,3,4로 나누었을 때 한 번도 나누어 떨어지지 않았으므로 소수
    • 4는 2로 나누었을 때 나누어 떨어졌으므로 소수가 아님.
  • 변수
    • input : 소수인지 판별하기 위해 입력 받은 숫자가 저장될 변수
    • i : input보다 1작은 수가 저장될 변수
    • j : 2부터 i까지 1씩 증가되는 숫자가 저장될 변수, 즉 i가 5이면 j는 2,3,4,5까지 차례로 변경됨
int main() {

  int input, i, j;

  scanf("%d", &input);

  i = input - 1;
  j = 2;

  while (1) {

    if (j <= i) {

      if (input % j == 0) {
        printf("소수 아님.");
        break;
      } else {
        j++;
      }

    } else {
      printf("소수.");
      break;
    }
  }
  return 0;
}

임의의 정수를 입력하여 소수를 판별(나누어 떨어질 때)

알고리즘의 이해

  • 소수를 구하는 또 하나의 알고리즘
  • 소수를 판별하는 숫자 n을 2부터 차례로 나누어서 처음으로 나누어 떨어졌을 때 n과 제수(나눈 수)가 같으면 소수.
    • 이 말은 중간에 한 번도 나누어 떨어진 수가 없었다는 것을 의미.
    • 1과 자기 자신을 약수로 갖는 소수라는 거.
  • 변수
    • input : 소수인지 판별하기 위해 입력 받은 숫자가 저장될 변수
    • i : 2부터 A까지 1씩 증가되는 숫자가 저장될 변수
int main() {

  int input, i;

  scanf("%d", &input);
  i = 2;                    // 제수

  while (input % i != 0)    
    i++;

  if (input == i)           // 제수 == 피제수
    printf("소수");
  else
    printf("소수 아님");

  return 0;
}

임의의 정수를 입력 받아 소수인지 판별(제곱근 이용)

알고리즘 이해

  • 어떤 수 n이 소수인지 판별하려면 n을 2부터 n의 제곱근까지의 숫자로 나누어 떨어지는지 검사하면 됨.
    • 제곱근까지의 수 중 한 개의 수에 대해서라도 나누어 떨어지면 소수가 아님
      • 예를 들어 25는 2,3,4,5로 나누었을 때 5로 나누어 떨어지므로 소수가 아님
      • 41은 2,3,4,5,6으로 나누어도 한 번도 나누어 떨어지지 않음. 소수
      • 제수는 1씩 증가, 제곱근 값의 소수 부분은 크기 비교시 의미 없음
    • 소수 판별을 그 수의 제곱근까지만 검사하는 이유는?

      • 예를 들어 25가 소수인지 알아 볼 때 25의 제곱근은 5, 2~5까지 나누어 보면됨
      • 즉, 25는 2x12.5 / 3x8.3 / 4x6.25 / 5x5 …
      • 5 이상의 숫자들로 나누면 5이하의 몫이 나오므로 위에서 실행한 계산이 반복됨.
        • 때문에 5보다 큰 숫자로는 나눌 필요 없음
  • 변수
    • input : 소수인지 판별하기 위해 입력 받은 숫자가 저장될 변수
    • i : input를 나누는 제수가 저장될 변수, 즉 i는 2부터 A의 제곱근까지 차례로 변경됨
#include<math.h>

int main() {

  int input, i;

  scanf("%d", &input);
  i = 2;                        // 제수

  while(1) {

    if (i <= sqrt(input)) {        // 제수가 제곱근보다 작은 동안에 나눠 떨어지면 소수아님
      if (input % i == 0) {
        printf("소수 아님.");
        break;
      } else {
        i++;
      }
    } else {                    // 위에서 안 나눠 떨어지고 제수가 제곱근보다 크면 소수
      printf("소수.");
      break;
    }
  }
  return 0;
}

임의의 정수를 입력 받아 그 안에 포함된 소수의 합을 구하시오. (소수의 합 구하기)
예를 들어 10을 입력 받았다면 2~10 사이에 속한 소수의 합을 계산.

알고리즘의 이해

  • 입력 받은 숫자 n의 범위에 속하는 모든 소수의 합계를 구하려면, 숫자를 2에서 n까지 증가시키면서 각각의 숫자가 소수인지 판별한 후, 그 수가 소수일 때만 합계에 누적하면 됨.
  • 변수
    • input : 소수를 구할 숫자 범위의 한계를 입력 받아 저장할 변수
    • sum : 소수가 누적될 변수
    • i : 소수인지 판별할 숫자가 저장될 변수. input으로 10을 입력 받았다면 K는 2~10까지 차례로 변경됨
    • j : 소수인지 판별할 때 사용할 제수가 저장될 변수
int main() {

  int input, sum, i, j;

  scanf("%d", &input);       // 소수를 구할 숫자의 범위를 입력 받음.

  sum = 0;
  i = 2;                     // 소수인지 판별할 숫자가 저장될 변수

  while(1) {

    j = 2;                   // 제수

    while (i % j != 0)
      j++;

    if (i == j)              // 나눠 떨어졌는데 제수와 피제수가 같다면(소수)
      sum += i;     

    if (i < input)           // 입력받은 a 보다 작으면 피제수를 키워서 반복
      i++;  
    else {
      printf("%d", sum);
      break;
    } 
  }
  return 0;
}

배열 A[99]에 2~100 사이의 정수를 기억시킨 후 이 배열을 이용하여 소수의 개수를 구하시오. (소수의 개수 구하기)

알고리즘의 이해

  • 배열에 들어 있는 연속된 숫자의 소수 여부를 판별하기 위해서는 정수의 수열에서 처음 나온 소수의 배수들은 소수가 아니라는 원리를 이용.
    • 배열의 첫 번째에 들어 있는 2는 소수지만 2의 배수들은 모두 소수가 아님.
    • 그러므로 2의 배수가 들어 있는 위치에는 모두 0을 채워 소수가 아님을 표시.
    • 소수의 판별은 배열의 해당 위치에 0이 들어 있는지만 확인하면 됨!
  • 변수
    • a[99] : 소수 여부를 판별할 숫자가 저장될 배열
    • k : 2부터 1씩 증가하는 값이 저장될 변수, 즉 K느 2,3,4,…,100까지 차례로 변경됨
    • i : 배열의 위치를 지정해주는 변수
    • j : 소수의 개수가 저장될 변수
    • m : 소수의 배수가 들어있는 위치를 지정할 변수
int main() {

  int k, i, j, m;
  int a[99];

  k = 1;
  do {

    k++;
    a[k - 2] = k; 

  } while(k < 100); // 2~ 100까지 값을 배열에 할당

  i = -1, j = 0;    // 배열 a의 첨자 i는 i++을 수행한 후 0이 되도록 -1로 초기화
                    // j는 소수의 개수를 담을 변수
  while (1) {

    i++;
    if (i > 98) {       // a[98](=100)을 넘으면 출력
      printf("%d", j);  // 소수의 개수 출력
      break;
    }

    if (a[i] == 0) {    // 값이 0이란 의미는 소수가 아니란 뜻이므로.
      continue;
    }

    j++;                // 소수+1
    m = i;              // 소수를 구한 현재 배열의 위치 i를 그 소수에 대한 배수가 들어 있는 위치를 계산하기 위해 m에게 줌

    while (1) {

      m += a[i];        // 현재 배열의 위치(m)에 이번에 찾은 소수(a[i])를 더해주면 그 소수의 배수가 들어있는 위치가 계산됨.

      if (m > 99)       
        break;
      a[m] = 0;         // 소수의 배수가 들어있는 위치에 0을 기억시켜 소수가 아님을 표시

    }
  }
  return 0;
}

두 수를 입력 받아 두 수의 최대공약수와 최소공배수를 계산해서 출력하시오.
(최대공약수, 최소공배수)

알고리즘의 이해

  • 최대공약수 - 공통으로 가지고 있는 약수 중 가장 큰 수
  • 최소공배수 - 두 수의 공통인 배수 중 가장 작은 수
  • 최대공약수와 최소공배수를 구할 두 수 중 큰 수와 작은 수를 정한 뒤 큰 수를 작은 수로 나누어 나머지를 구한다.
  • 이 때 나머지가 0이면 그 때의 작은 수가 최대공약수이고, 원래의 두 수를 곱한 값을 최대공약수로 나눈 값이 최소공배수이다.
  • 만약 큰 수를 작은 수로 나누었을 때 나머지각 0이 아니면, 그 때의 작은 수를 큰 수로 하고 나머지를 작은 수로 하여 나머지가 0이 될 때까지 반복.
    • 이와 같이 큰 수를 작은 수로 나눠 나머지가 0이 될 떄까지 반복하여 최대공약수를 구하는 방법을 유클리드 호제법이라 함.
  • 변수
    • a : 최대공약수와 최소공배수를 구하기 위해 입력 받은 첫 번째 수가 저장될 변수
    • b : 최대공약수와 최소공배수를 구하기 위해 입력 받은 두 번째 수가 저장될 변수
    • big : 입력 받은 수 중 큰 수가 저장될 변수
    • small : 입력 받은 수 중 작은 수가 저장될 변수
    • mok : 큰 수를 작은 수로 나눈 몫이 저장될 변수
    • nmg : 큰 수를 작은 수로 나누었을 때 나머지가 저장될 변수
    • gcd(Greatest Common Divisor) : 최대공약수가 저장될 변수
    • lcm(Least Common multiple) : 최소공배수가 저장될 변수
  • INT()로 나머지 구하기
    • 15/12의 나머지 3 구하기
    • 15를 12로 나눠 정수를 취하면 1
    • 몫을 제수와 곱한 후 피제수에서 빼면 나머지를 구할 수 있음
      • 15 - (1x12) -> 3
int main() {

  int a, b, big, small, mok, nmg, gcd, lcm;

  scanf("%d %d", &a, &b);

  if (a >= b) {       // 입력 받은 a, b 중 큰 수를 big에 작은 수를 samll에 할당
    big = a;
    small = b;
  } else {
    big = b;
    small = a;
  }

  while (1) {
    mok = big / small;
    nmg = big - mok * small;    // 나머지 = 큰수 - (몫*작은수)

    if (nmg == 0) {             // 나머지가 0 일 때,
      gcd = small;              // 작은수가 gcd(최대공약수)
      lcm = a * b / gcd;        // lcm(최소공배수)는 첫큰수*첫작은수/gcd
      printf("%d %d", gcd, lcm);
      break;
    }

    big = small;                // 나머지가 0이 아니면 작은수를 큰수로
    small = nmg;                // 나머지를 작은 수로해서 반복
  }

  return 0;
}

정수를 입력 받아 약수를 구해 출력(약수 구하기)

알고리즘의 이해

  • 약수 - 어떤 수를 나누어 나머지가 없게 할 수 있는 수.
    • 즉, 나머지가 0이라는 의미. 예를들어 10의 약수는 1, 2, 5, 10.
  • 어떤 수 n을 1부터 n까지 차례대로 나누어 나머지가 0이되게 하는 제수들이 n의 약수.
  • 변수
    • divisor[100] : 구한 약수가 저장될 1차원 배열
    • input : 약수를 구하기 위해 입력 받은 숫자가 저장될 변수
    • c : 1씩 증가되는 제수가 저장될 변수. 즉 c는 1부터 b까지 차례로 변경됨
    • d : 약수가 저장될 divisor배열의 위치를 지정해 주는 변수. 즉 D는 1부터 약수의 개수까지 차례로 변경됨
    • mok : 나머지를 구하기 위해 입력 받은 수 b를 c로 나눈 몫이 저장될 변수
    • nmg : 입력 받은 숫자 b를 c로 나눈 나머지가 저장될 변수
    • i : 약수를 출력할 때 배열의 위치를 지정해 주는 변수
int main() {

  int input, mok, nmg, i;          
  int divisor[100];           // 약수를 저장할 배열

  scanf("%d", &input);        // 약수를 구할 숫자를 input에 입력받음.
  int c = 0, d = -1;          // c는 제수 d는 약수 배열 인덱스

  while (1) {
    
    c++;

    if (c <= input) {         // 입력받은 input까지 제수로 나눴을 때 나머지가 0인 약수를 구함
      mok = input / c;
      nmg = input - mok * c;
      if (nmg == 0) {
        d++;
        divisor[d] = c;
      }
    } else {
      printf("%d ", input);
      for (i = 0; i <= d; i++) {
        printf("%d ", divisor[i]);
      }
      break;
    }
  }
  return 0;
}

정수를 입력 받아 소인수를 구해 출력(소인수 분해)

알고리즘의 이해

  • 소인수 - 어떤 수를 구성하는 인수 중에서 소수인 것.
    • 어떤 수의 약수중에서 소수인 녀석들만 가리켜서 소인수라 칭함!
  • 소인수 분해 - 소수를 이용하여 그 수를 만드는 곱의 형태로 표현하는 것.
    • 예를 들어 20의 약수는 1, 2, 4, 5, 10, 20 이지만 1, 4, 10, 20은 소수가 아님.
    • 소인수는 2, 5.
    • 그러므로 20을 소인수 분해하면 2 x 2 x 5.
  • 어떤수 n을 소인수 분해하려면 n을 2부터 차례대로 n의 제곱근까지의 숫자로 나누어 떨어지는지 검사하면 됨.
    • 제곱근까지의 수 중 n을 처음으로 나누어 떨어지게 하는 수가 있으면 그 수는 소수!
      • n의 소인수가 됨.
    • 소인수를 구했으면 그 때의 몫을 n로 하여 2부터 다시 n의 제곱근까지의 숫자로 나누는 작업을 반복
    • 만약 제수가 n의 제곱근보다 커지면 그때는 몫인 n자체가 그 수의 소인수가 된다.
  • 변수
    • p[100] : 소인수가 저장될 배열
    • n : 소인수로 분해하기위해 입력 받은 숫자가 저장 될 변수
    • pIndex : 소인수로 저장할 배열 p의 위치를 지정해 주는 변수
    • d : 제수가 저장될 변수. 2부터 입력 받은 수 B의 제곱근까지 1씩 증가하는 숫자가 저장됨
    • e : 입력 받은 수 n의 제곱근이 저장될 변수, 즉 n가 20이면 e에는 4가 저장됨
    • mok : n을 d로 나눈 몫이 저장될 변수
    • nmg : n을 d로 나눈 나머지가 저장될 변수
int main() {

  int n, pIndex, d, e, mok, nmg;
  int p[100];

  scanf("%d", &n);       // 정수 n을 입력받아서 소인수 구해 출력

  pIndex = -1;           // 소인수를 저장할 배열의 인덱스
  d = 2;                 // 제수

  while (1) {
    e = (int)sqrt(n);    // 입력 받은 값 n의 제곱근(e)을 구해 정수로 변환
  
    while (1) {
      if (d > e) {       // 제수가 n의 제곱근(e)보다 커지면 
        d = n;           // 피제수(n) 자체가 소인수
        break;
      }

      mok = n / d;
      nmg = n - mok*d;

      if (nmg == 0)       // 처음으로 나머지가 0이 되게 하는 수가 그 수의 소인수.
        break;
      else
        d++;              // d를 2부터 b의 제곱근(e)까지 증가 시키기 위해 1을 더함.
    }

    pIndex++;             // pIndex늘리고
    p[pIndex] = d;        // 소인수를 저장하는 배열p[]에 제수 저장

    if (n == d)           // 제수가 입력값과 같으면 소인수분해 과정을 모두 마친 것.
      break;
                          // 소인수를 구하고 분해과정이 모두 안끝났다면(n != d)
    n = mok;              // 그 때의 mok을 n로 하여 반복
  
  }

  for (int i = 0; i <= pIndex; i++) {    // 소인수 출력
    printf("%d ", p[i]);
  }


  return 0;
}

10진수를 입력 받아 2진수로 변환하시오. 단, 1000이하의 숫자를 입력 받음.
(진법변환 - 10진수를 2진수로 변환)

알고리즘의 이해

  • 10진수를 2진수로 변환하려면 10진수를 2로 나누어 나머지를 구한 후 저장,
    • 다시 몫을 2로 나누어 나머지를 구해 저장하는 과정을 반복.
    • 몫이 0이 될 때까지 이 작업을 반복
    • 마지막에 구한 나머지부터 거꾸로 출력.
    • 1000이하의 숫자는 2진수로 변환할 때 10개의 요소를 갖는 배열이면 충분
  • 변수
    • b[10] : 입력 받은 10진수를 2로 나눈 나머지(2진수)가 저장될 배열
    • decimal : 입력 받은 10진수가 저장될 변수
    • decimal_copy : 입력 받은 10진수를 그대로 출력하기 위해 b가 저장될 변수
    • bIndex : 배열의 위치를 지정해주는 변수
    • mok : b를 2로 나눈 몫이 저장될 변수
    • nmg : b를 2로 나눈 나머지가 저장될 변수
    • i : 배열을 출력할 때 배열의 위치를 지정해 주는 변수
int main() {

  int decimal, decimal_copy, bIndex, mok, nmg, i;
  int b[10];

  scanf("%d", &decimal);
  decimal_copy = decimal;        // 원래 입력값을 나중에 출력하기 위해 복사
  // c언어는 배열이 0부터 시작, bIndex++를 수행하고 0이되도록 -1로 초기화.
  bIndex = -1; 

  do {
    bIndex++;
    mok = decimal / 2;
    nmg = decimal - mok*2;

    b[bIndex] = nmg;             // 2로 나눈 나머지를 b배열에 저장
    decimal = mok;               // 몫을 10진수로 하여 2로 못 나눌 떄까지 반복
  } while (mok != 0);

  printf("%d ", decimal_copy);   // 원래 입력값 출력

  // 배열의 위치가 0 부터 시작하기 때문에 
  // 첨자 i는 2진수가 저장된 마지막 위치인 bIndex부터 0이 될때까지 반복 수행
  for (i = bIndex; i >= 0; i—)  
    printf("%d", b[i]);

  return 0;
}

배열의 끝 쪽부터 저장하기

  • 2로 나눈 나머지를 배열의 끝 쪽부터 저장하면 출력할 때 앞에서부터 차례로 출력 가능
  • 2진수를 뒷자리부터 구하지만 출력할 떄는 앞자리부터 출력한다는 것과 배열에서 2진수가 들어 있는 첫 번째 위치를 계산하는 것이 이 알고리즘의 핵심
  • 변수 k 추가
    • k : 맨 처음 출력할 배열의 위치를 지정해 주는 변수
...
int k;
...  
  do {
    bIndex++;
    mok = decimal / 2;
    nmg = decimal - mok*2;
    b[9 - bIndex] = nmg;    // 배열의 뒤쪽에서부터 차례로 저장되도록 처리
    decimal = mok;
  } while (mok != 0);

  printf("%d ", decimal_copy);
  k = 9 - bIndex;          // 2진수가 들어 있는 첫 번째 위치를 계산.
       
  for (i = k; i < 10; i++)  
    printf("%d", b[i]);
...



정보처리기사실기