• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

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

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

06 Aug 2018

Reading time ~10 minutes

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


0 또는 1로 입력되는 다섯 개의 숫자를 배열에 입력 받아 보수와 2의 보수를 구하시오.
(1의 보수, 2의 보수 구하기)

알고리즘의 이해

  • 1의 보수는 0은 1로 1은 0으로 변경하면 됨
    • 1에서 변환할 값을 빼면 됨
  • 2의 보수는 1의 보수에 1을 더하면 됨
    • 1의 보수에 1을 더함, 맨 오른쪽 요소에 1을 더해서 2의 보수 배열에 저장.
      • 즉 01100의 1의 보수는 10011
      • 2의 보수는 10100
  • 1의 보수에 1을 더해 발생하는 자리올림수를 처리하는게 이 알고리즘의 핵심
    • 2진수 이므로 1보다 큰 값이 있으면 안됨, 2로 나눠 나머지 0만 남김.
    • 자리 올림이 발생했는지 확인.
  • 1의 보수를 2의 보수로 변환하는 다른 방법
    • 2의 보수를 구하는 다른 방법은 1의 보수의 맨 오른쪽에서 왼쪽 방향으로 0이 나올 때까지 반대로 써주고 나머지는 그대로 써주면 됨.
    • 이진수를 1의 보수로 변환하는 방법
      • 이진수 : 00011000
      • 1의 보수 : 11100111
      • 2의 보수 : 11101000
        • 1의 보수의 맨 오른쪽에서 왼쪽 방향으로 0 이 나올 때까지 반대로 씀.
  • 변수
    • a[5] : 한 자리씩 입력 받은 이진수 5자리가 저장될 배열
    • b1[5] : 1의 보수가 저장될 배열
    • b2[5] : 2의 보수가 저장될 배열
    • i : 배열의 위치를 지정해주는 변수
    • c : 자리올림수가 저장될 변수
main() {
  int a[5], b1[5], b2[5];       // a 이진수, b1 1의 보수, b2 2의 보수
  int i, c = 1;                 // c는 자리올림수

  for(i = 0; i < 5; i++) {
    scanf("%d", &a[i]);
    b1[i] = 1 - a[i];           // 이진수 각 자리수를 1에서 빼면 1의 보수
  }

  for(i = 5; i >= 0; i--) {
    b2[i] = b1[i] + c;          // 맨 오른쪽 요소부터 자리올림수(1)를 더해 2의 보수를 만듦
    b2[i] = b2[i] % 2;          // 2진수이므로 해당 2의 보수 자리수가 2를 넘으면 0으로 만듦
    c = b1[i] * c;                
    // 자리올림이 발생하는 경우는 현재 자리올림수가 1이고, 1의보수 현재 위치 값이 1인 경우
    // 두 수를 곱하면 자리 올림수가 결정됨
  }

  for (int k = 0; k < 5 ; k++)
    printf("%d", a[k]);         // 입력된 이진수 출력
  printf(" ");
  for (int k = 0; k < 5; k++)  
    printf("%d", b1[k]);        // 1의 보수 출력
  printf(" ");
  for (int k = 0; k < 5; k++)
    printf("%d", b2[k]);        // 2의 보수 출력
}

2의 보수 쉽게 구하기

알고리즘의 이해

  • 2의 보수를 구하는 다른 방법 한 가지는 이진수의 맨 오른쪽에서 왼쪽 방향으로 1이 올때까지 그냥 써주고 나머지는 반대로 써주면 됨.
    • 이진수 : 01100
    • 2의 보수 : 10100
      • 100까진 그대로, 앞 01은 반대로 = 10100
  • 변수
    • a[5] : 이진수 자료가 저장될 배열, 01010이 저장되었다고 가정
    • b[5] : 2의 보수가 저장될 배열
    • i, j : 배열의 위치를 지정해주는 변수
main() {

  int i, j;
  int a[5] = { 0, 1, 0, 1, 0 }, b[5];

  for (i = 4; i >= 0; i--) {
    if (a[i] == 1)              // 1이 나올 때부터 반대로 바꿈
      break;
    b[i] = a[i];                // 1이 안나오면 그대로 
  }

  b[i] = a[i];                  // 만난 시점까진 그대로 적어주고 그 다음부터
  for (j = i-1; j >=0; j--)     
    b[j] = 1 - a[j];            // 반대로 바꿈
  
  for(int k =0; k < 5;k++)
    printf("%d", a[k]);         // 이진수 출력
  printf(" ");
  for(int k = 0; k < 5; k++)
    printf("%d", b[k]);         // 2의 보수 출력
}

2의 보수로 표현된 값의 2의 보수를 구하시오. (보수의 보수 구하기)

  • 알고리즘의 이해
    • 2의 보수의 보수 구하기는 바로 위 문제처럼 구할 수 있지만 여기선 다른 방법 이용.
    • 보수는 음수를 표현할 때 사용.
    • 보수로 표현된 수치에 대한 10진수 값을 알아내려면 보수로 표현된 수치를 다시 보수로 표현해야 함.
    • 위에서 학습한 2의 보수 구하는 과정을 역으로 진행하면 2의 보수에 대한 2의 보수를 구할 수 있음.
      • 즉, 2의 보수는 ‘2진수’ -> ‘1의보수(0은 1, 1은 0)’ -> ‘2의보수(1의 보수 +1)’ 과정을 통해 구함.
      • 역 과정 ‘2의 보수’ -> ‘1의 보수(2의보수 -1)’ -> ‘이진수(0은 1, 1은 0)’ 을 통해 2의 보수에 대한 보수, 즉 원래 이진수를 구할 수 있음.
      • 예)
        • 2의 보수 : 11111010
        • 1의 보수로 변환 : 11111001
        • 1의 보수에 대한 1의 보수(2진수)를 구함 : 00000110
        • 10진수로 변환하고 음수 부호를 붙임 : -6
    • 배열의 각각의 요소에 1자리씩 저장된 2진수에서 1을 빼는 과정은 오른쪽에서부터 첫 번째 1이 나올 때까지는 0을 1로 바꿔주고 첫 번째로 나오는 1은 0으로 바꿔줌. 그리고 그 왼쪽부터는 그대로 옮겨줌.
      • 첫 번째 1을 찾고, 첫 번째 1이 나올 때 까지 0을 1로 바꿔주고 1을 0으로 바꿔줌.
    • 맨 오른쪽에서 부터 확인, 첫 번째로 1이 나왔을 때는 감수를 0으로 하여 피감수인 2의 보수를 그대로 1의 보수 배열에 전달.
    • 피감수가 0이고 감수가 1이면 아직 첫 번째로 나온 1이 아니므로 피감수 0을 1로 변환해서 1의 보수 배열에 저장시켜야 함.
    • 1의 보수를 원래의 수로 만든다는 것은 1의 보수를 다시 1의 보수로 표현하면 됨
    • 실기 문제로 2진수를 10진수로 변환하는 문제가 출제 가능,
      • 2진수 첫 번째 비트가 0이면 양수, 그냥 10진수로 변환
      • 2진수 첫 번째 비트가 1이면 음수, 2의 보수로 표현된 것이니 10진수로 변환하고 마이너스를 붙임
  • 변수
    • a[5] : 한 자리씩 입력 받은 2의 보수 5자리가 저장될 배열
    • b[5] : 2의 보수의 1의 보수가 저장될 배열
    • i : 배열의 위치를 지정해 주는 변수
    • c : 감수가 저장될 변수
#include <stdio.h>
#include <math.h>

main() {

  int a[5], b[5];
  int i, c;

  for (i = 0; i <= 4; i++)
    scanf("%d", &a[i]);             // 2의 보수를 입력 받음

  c = 1;                            // 초기 감수값을 1로 지정

  for (i = 4; i >= 0; i--) {  
    b[i] = a[i] - c;                // 2의 보수에 1을 빼면 1의 보수
    if (a[i] == 0 && c == 1)        // 2의 보수가 1을 안만났다면 감수 1로 유지, 만나면 0으로 바꿈.
      c = 1;
    else
      c = 0;
    b[i] = abs(b[i]);               // 감수를 빼서 -1일 때 1로 바꿔줌.
  }

  for (i = 0; i <= 4; i++)          // 1의 보수의 1의 보수는 이진수
    b[i] = 1 - b[i];

  for (int k = 0; k <= 4; k++)
    printf("%d", a[k]);             // 2의 보수 출력
  printf(" ");
  for (int k =0; k <= 4; k++)
    printf("%d", b[k]);             // 이진수 출력
}

0 또는 1로 입력되는 다섯 개의 숫자를 배열에 입력 받아 그레이 코드 또는 이진수로 변환하시오.
(그레이 코드 변환하기)

알고리즘의 이해

  • 그레이코드 : 이진법 부호의 일종으로, 연속된 수가 1개의 비트만 다른 특징을 지님. 연산에는 쓰이진 않고 주로 데이터 전송, 입출력 장치, 아날로그-디지털 간 변환가 주변장치에 쓰임.
  • xor연산 : 연산하는 두 개의 값이 같으면 0, 다르면 1을 출력
  • 이진수를 그레이 코드로 변환하기
    • 첫 번째 그레이 비트는 이진수 비트를 그대로 내려쓰고, 두 번째 그레이 비트부터는 변경할 이진수의 해당 번째 비트와 그 왼쪽 비트를 xor연산하여 씀. (이진수만 xor 연산)
    • 이진수 1011을 입력 받았다면?
      • 그레이코드 : 1110
      • 첫 번째 비트 그대로 씀, 그리고 이진수 오른쪽에 있는 해당 번째 비트와 xor연산
  • 그레이 코드를 이진수로 변환하기
    • 첫 번째 이진수 비트는 그레이 비트를 그대로 내려 씀, 두 번째 이진수 비트부터는 왼쪽에 구해 놓은 2진수 비트와 변경할 그레이 코드의 해당 번째 비트를 xor연산해서 씀. (이진수와 그레이비트 xor)
    • 그레이코드 1001을 입력 받았다면?
      • 이진수 : 1110
  • 변수
    • input[5] : 한 자리씩 입력 받은 이진 자료가 저장될 배열
    • cont[4] : 이진수나 그레이 코드로 변환된 자료가 저장될 배열
    • i : 배열의 위치를 지정해주는 변수
  • 조건
    • 배열의 첫 번째 비트가 0이면 나머지 네 개의 비트는 이진수므로 그레이 코드로 변환하여 출력
    • 배열의 첫 번째 비트가 1이면 나머지 네 개의 비트는 그레이코드이므로 이진수로 변환하여 출력
#include <stdio.h>

main() {

  int i;
  int input[5], cont[4];

  printf("숫자 5개 입력, 첫번째가 숫자가 0이면 이진수->그레이코드, 1이면 그레이코드->이진수 : \n");
  for (i = 0; i <= 4; i++) {
    scanf("%d", &input[i]);
  }
  cont[0] = input[1];                         // 첫 번째 비트는 그대로 가고

  if (input[0] == 1) {                        // 그레이코드 -> 이진수
    for (i = 0; i <= 2; i++) {
      if (input[i + 2] == cont[i])            // 그레이코드 비트와 이진수 옆 비트 xor연산
        cont[i + 1] = 0;
      else
        cont[i + 1] = 1;
    }
  } else {                                    // 이진수 -> 그레이코드
    for (i = 0; i <=2; i++) {
      if (input[i + 1] == input[i + 2])       // 이진수 비트끼리 xor연산
        cont[i + 1] = 0;
      else
        cont[i + 1] = 1;
    }
  }

  if (input[0] == 1)
    printf("이진수 ");
  else
    printf("그레이코드 ");

  for (i = 0; i <= 3; i++)
    printf(" %d ", cont[i]);
}

100건 이내의 12자리로 구성된 숫자를 더하는 순서도를 작성하시오. (큰 수 더하기 - 10진수 더하기)

  • 단, 12자리의 숫자는 각 자리가 분리되어 배열에 입력됨. 예를들어 999,999,999,999라면 999999999999와 같이 입력됨.
  • 단, 배열의 첫 번째 요소로 0을 입력 받으면 계산 후 결과를 출력하고 프로그램을 종료한다.
  • 단, 결과 값이 들어갈 배열에는 초기값으로 0이 들어 있다고 가정한다.

알고리즘의 이해

  • 정수의 범위에서 벗어나는 큰 수를 더하면 실수로 계산되어 오차가 발생.
  • 이런 경우 각각의 자릿수를 분리하여 덧셈하면 아무리 큰 수라도 자릿수에 관계없이 오차가 발생하지 않게 계산 가능.
  • 예를 들어 987 + 999를 각각의 자릿수를 분리하여 자릿수끼리 더하면
    • 9    8    7
    • 9    9    9
    • 18 17 16 // 1을 하나씩 올림
      • 여기서 16은 그냥 16, 17은 170, 18은 1800이 됨.
      • 10을 윗자리로 올려서 6, 18
      • 10을 다시 윗자리로 올려서 8, 19
      • 10을 다시 윗자리로 올려서 9가 되고 윗자리 1이 됨.
    • 1 9 8 6
      • 이 원리를 이용하면 아주 큰 정수를 오차 없이 더할 수 있음.
  • 변수
    • a[12] : 입력 받은 숫자를 각각의 자리로 분리하여 저장할 배열, 즉 253을 입력 받으면 a[1]에 2, a[2]에 5, a[3]에 3을 각각 저장
    • b[14] : a배열에 저장된 숫자를 각각의 자릿수별로 누적할 배열, 100건 이내의 자료를 누적하므로 a배열보다 자릿수가 2자리 많음. b 배열에는 초기 값으로 0이 들어 있음.
    • i : 배열의 위치를 지정해 주는 변수
    • j : 배열의 시작 위치를 지정해 주는 변수
    • mok : 배열 각 자리에 있는 값을 10으로 나눈 몫이 저장될 변수
    • nmg : 배열 각 자리에 있는 값을 10으로 나눈 나머지가 저장될 변수
#include<stdio.h>

main() {

  int i, j, mok, nmg;
  int a[12], b[14] = { 0 };           // 문제에서 결과값이 저장될 배열 b, 초기값 0으로 초기화

  while (1) {
    for (i = 0; i <= 11; i++)
      scanf("%d",&a[i]);              // 숫자 하나씩 입력

    if (a[0] == 0)                    // 0을 a[0]에 입력받으면 입력 중지
      break;
    else
      for (i = 0; i <=11; i++)        // 입력받은 값을 b에 저장, 자릿수가 올라갈 수 있어서 앞 2자리 비워둠
        b[i + 2] += a[i];
  }

  for (i = 13; i >= 1; i--) {
    mok = b[i] / 10;
    nmg = b[i] - mok * 10;            // 각 칸별로 10으로 나눔, 몫을 윗자리에 더함
    b[i] = nmg;                       // 나머지만 남김
    b[i - 1] += mok;        
  }

  if (b[0] == 0)                      // b[0] 첫자리가 0이라면(수가 크지 않으면)
    j = 1;                            // 출력을 위해 사용하는 인덱스 j를 1로 바꿈
  else                                // 입력된 수가 커서 첫 자리부터 수가 시작되면 j를 0으로 가리킴
    j = 0;

  for (i = j; i <= 13; i++)
    printf("%d ", b[i]);

}

배열 x(10)과 y(10)에 이진수가 각각 입력되어 있다. 두 이진수의 덧셈 결과를 이진수 형태로 출력하는 순서도를 작성하되 덧셈의 결과 MSB(최상위 비트)에서 올림수가 발생하면 “OVERFLOW!!”를 출력한다.
(이진수 더하기)

알고리즘의 이해

  • 최상위 비트는 맨 왼쪽의 비트를 말함. 맨 왼쪽의 비트에서 자리올림이 발생하면 자리올림수를 저장할 공간이 없기 때문에 overflow 출력.
    • overflow : 연산의 결과값이 기억 용량을 초과하여 넘쳐나는 상태
  • 한 자리 이진수 세 개를 더할 때 결과가 10이면, 합이 10이 아니고 합은 0, 자리 올림수가 1
  • 이진수를 덧셈할 때는 각각을 분리해서 각각의 배열에 넣은 다음 오른쪽 끝에서 왼쪽 방향으로 진행하면서 한 자리씩 더하면 됨.
  • 오른쪽에서 왼쪽으로 진행하는 이유는 자리올림을 고려해야 하기 때문. 각각의 자리를 더할 때 결과가 2 이상이면 자리올림이 발생한 것.
    • 자리올림수를 그 왼쪽의 자리를 덧셈할 때 같이 더해줌
  • 변수
    • b1[10], b2[10] : 덧셈할 이진수가 저장되어 있는 배열
    • a[10] : 덧셈의 결과가 저장될 배열
    • z : 자리올림수 발생 여부를 판단하기 위한 변수
    • c : 자리올림수가 저장될 변수
    • j : 배열의 위치를 지정해 주는 변수
main() {
  int i, z, c, j;             // c는 자리올림수, z는 자리올림수 발생 여부 판단하는 변수
  int b1[10], b2[10], a[10];  // 입력받을 2진수 배열 두개 b1, b2, 합을 저장할 배열 a

  for (i = 0; i <= 9; i++)
    scanf("%d", &b1[i]);
  for (i = 0; i <= 9; i++)
    scanf("%d", &b2[i]);

  z = 0, c = 0, j = 9;      // 배열의 끝위치를 지정할 첨자 j.(이진수의 맨 우측부터 합할테니까)

  do {

    z = b1[j] + b2[j] + c;  
    if (z < 2) {            // 배열 한칸의 합 + 자리올림수 가 2보다 작으면 그냥 저장
      c = 0;
      a[j] = z;
    } else {                // 2보다 클 경우 
      c = 1;
      a[j] = z - 2;         // 2진수 이므로 자리올림수를 뺀 나머지를 저장
    }
    j--;
  } while (j >= 0);

  if (c == 0) {
    do {
      j++;
      printf("%d", a[j]);
    } while(j < 9);
  } else                    // 자리올림수 c 가 1일 경우 배열의 크기를 초과하므로 오버플로우 출력
    printf("OVERFLOW!!");
}
  • 결과에서 2를 빼는 이유는 자리올림수를 제외한 나머지 값을 합계 배열에 저장하기 위해서
    • 10진수의 자리올림수 1이 10이듯 2진수의 자리올림수 1은 2.


정보처리기사실기