정보처리기사 실기 알고리즘 정리 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자체가 그 수의 소인수가 된다.
- 제곱근까지의 수 중 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]);
...