정보처리기사 실기 알고리즘 정리 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을 더함, 맨 오른쪽 요소에 1을 더해서 2의 보수 배열에 저장.
- 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.