2009년 04월 29일
사이냅소프트 채용퀴즈 4번
두개의 이진수에 대한 곱하기 프로그램을 작성하라.
Input
텍스트 파일을 입력으로 한다.(텍스트 파일은 q4_input.txt 로 한다.)
30자리 이하의 이진수 두개를 입력받는다. 두개의 이진수는 공백으로 분리하고 각 행은 줄바꿈 문자로 구분한다. “0 0”을 만나면 프로그램을 종료한다.
Output
다음과 같은 형태로 출력 한다. 곱하기의 단계별 절차를 모두 출력한다. 공백라인으로 각 출력결과를 구분한다.
Sample Input: Output for Sample Input:
11 11
111 10
0 0
Output for Sample Input:
11
11
--
11
11
----
1001
111
10
---
000
111
----
1110
4번 문제는 마지막으로 풀었던 문제입니다.
어려웠습니다...흑흑.
다음은 이번 문제에 사용된 함수들입니다.
__int64 col_binary(char *oper1, char *oper2) // 읽어들인 문자열로 곱하기 연산을 하는 함수
{
__int64 oper_num1, oper_num2;
oper_num1 = change_deci(oper1); // 문자열을 __int64형으로 변환시켜 저장합니다.
oper_num2 = change_deci(oper2);
return oper_num1 * oper_num2; // 계산하여 리턴합니다.
}
int change_deci(char *operand) // 읽어들인 문자열을 int형으로 변환하는 함수
{
int result = 0;
int len, i, tmp;
len = strlen(operand);
for(i = 0; i < len; i++)
{
tmp = (*(operand + i) - '0');
result += pow(2, len-i-1) * tmp;
}
return result;
}
int pow(int x, int y) // 제곱을 구하는 함수 math.h에 있는 함수는 double형이라 int형으로 만들었습니다.
{
int r = 1;
for(int i=0; i<y; i++)
{
r *= x;
}
return r;
}
int cal_place_num(__int64 number)
{
int result;
__int64 temp;
temp = number;
for(result = 2; ; result++) // '/'연산이 1번 이루어지면 자릿수는 2가 되기 때문에 result는 2부터 시작한다.
{
temp /= 2;
if( temp == 1 ) // 몫이 1이 되면 for문을 중단한다.
break;
}
return result;
}
void print_space(int len, FILE *outfile) // 주어진 길이만큼 공백을 출력합니다.
{
for(int i = 0; i < len; i++)
fprintf(outfile, " ");
}
void print_hyphen(int len, FILE *outfile) // 주어진 길이만큼 '-'을 출력합니다.
{
for(int i = 0; i < len; i++)
fprintf(outfile, "-");
fprintf(outfile, "\n");
}
일단 이진수를 문자열로 입력받고 그것을 10진수로 바꾸어 계산합니다.
그 결과값으로 이진수 결과값의 자릿수를 구하고 메모리를 할당합니다.
oper_len1 = strlen(oper1);
oper_len2 = strlen(oper2);
result = cal_binary(oper1, oper2);
oper_col_len = cal_place_num(result);
result_bin = (int*) malloc( oper_col_len * sizeof(int));
oper3 = (int **) malloc( oper_len2 * sizeof(int *));
그리고 oper3는 계산과정을 보여주기 위한 더블 포인터 변수입니다.
아...더블 포인터 변수는 좀 다루기가 까다롭습니다.
포인터와 별로 다를게 없는것 같으면서도 생각을 더 많이 하게 됩니다. 안어려운듯...하면서도 어려운듯...하면서 또 아닌듯...
space_len1 = oper_col_len - oper_len1; // 출력시 숫자 앞의 공백을 구합니다.
space_len2 = oper_col_len - oper_len2;
다음으로 필요한 것은 예제에 나온 것처럼 숫자 앞의 공백을 넣기 위해 구해야 합니다.
그리고 출력할때는
print_space(space_len1, outfile);
fprintf(outfile, "%s\n", oper1);
print_space(space_len2, outfile);
fprintf(outfile, "%s\n", oper2);
print_space((space_len1 < space_len2) ? space_len1:space_len2, outfile);
print_hyphen((oper_len1 > oper_len2) ? oper_len1:oper_len2, outfile); // '-'을 출력합니다.
요렇게.
oper_col_len변수는 결과값을 이진수로 하였을 때 자릿수 입니다.
예제를 보면 '-'이나 공백이 결과값의 자릿수로 정해집니다. 꼭 필요합니다.
자. 이제 결과값은 알고 있습니다.
그럼 계산 과정을 출력해야 하지요.
근데 그게 설명하기가 참 복잡합니다.
대충 설명하자면
계산 과정의 단계는 두 번째 읽은 수의 자릿수에 의해 결정됩니다.
때문에 oper3에 메모리를 할당할 때 oper_len2만큼 할당하였고,
두 번째 할당할 때에는 oper_col_len만큼 할당하였습니다.
저는 이 2차원 배열인 oepr3에 계산 과정을 저장하였고, 그것을 출력하는 방법을 사용하였습니다.
그리고 결과값을 이진수로 나타내는것은 이미 주어진 결과값을 이진수형태로 바꾸는 방법을 사용할 수 있었지만,
문제의 의도는 그것이 아니라고 생각하여
oper3에 저장된 값을을 손으로 하는 것처럼 계산하여 결과값을 얻었습니다.
for(i = 0, j = oper_len2 - 1, l = oper_col_len - oper_len1; i < oper_len2; i++, j--, l--)
{
*(oper3 + i) = (int *) malloc( oper_col_len * sizeof(int));
for( k = 0; k < oper_col_len; k++) // 배열의 모든 값을 0으로 초기화합니다.
*(*(oper3 + i) + k) = 0;
for(k = 0; k < oper_len1; k++)
{
*(*(oper3 + i) + l + k) = (*(oper2 + j) - '0' ) * (*(oper1 + k) - '0');
//printf("%d = %d * %d\n", *(*(oper3 + i) + l + k), *(oper2 + j) - '0', *(oper1 + k) - '0');
//printf("i = %d, j = %d, k = %d\n", i, j, k);
}
}
for(i = 0; i < oper_len2; i++) // 계산 과정을 프린트 합니다.
{
print_space(space_len1 - i, outfile); // 앞 부분의 공백을 출력 합니다.
for(j = space_len1 - i; j < oper_len1 + space_len1 - i; j++) // 1이 시작되는 부분부터 피연산자의 자리수만큼 출력합니다.
{
fprintf(outfile, "%d", *(*(oper3 + i) + j));
}
fprintf(outfile, "\n");
}
print_hyphen(oper_col_len, outfile);
fprintf(outfile, "\n");
for(i = oper_col_len - 1; i >= 0 ; i--)
{
int temp = 0;
for(j = 0; j < oper_len2; j++)
{
temp += *(*(oper3 + j) + i); // 각 자리수의 합을 구합니다.
}
if( (temp / 2) > 0 )
*(*(oper3 + 0) + (i-1) ) += temp / 2; // 그 합들 2로 나누었을 때 몫이 1 이상이라면 다음 자리수의 첫 번째 위치에 더합니다.
*(result_bin + i) = temp % 2; // 자리수의 합을 2로 나눈 나머지를 저장합니다.
}
for(i = 0; i < oper_col_len; i++) // 결과값들을 이진수로 출력합니다.
fprintf(outfile, "%d", *(result_bin +i));
fprintf(outfile, "\n\n");
설명하기가 귀찮아서압박시러워서;; 걍 그 부분의 소스코드를 다 올립니다.
어렵지 않은건데 이 코드가 나오기까지 머리를 2시간 넘게 쥐어 뜯으며 만들었습니다.
이렇게 해보다 저렇게 해보다 안되니깐 다시 다른방법으로 해보고....
각 for문의 초기값, 조건식, 증감식을 정하는데 좀 애를 먹었습니다.
4번 문제는 가장 마지막으로 풀었던 문제입니다.
7번보다 어려웠어요 ;ㅇ;
그리고 저에게 실패를 안겨준 문제 였습니다.
저로선 참 힘들게 풀었는데 코드에 오류가 딱 한줄이 있었습니다.
result_bin에 메모리를 할당할 때 크기를 oper_col_len으로 했어야 하는데 어째서인지 oper_len2로 되어있었습니다. ㅜㅜ
찾느라 엄청 헤멧었지요.
그리고 문제 조건을 보면 '30자리 이하의 이진수'이기 때문에 2^30 * 2^30이면 2^60이 됩니다.
32비트 int형에서는 오버 플로우 입니다.
이것을 미처 생각하지 못하였습니다.
지금까지 64비트형 int를 쓴 일이 없다보니...그저 구차한 변명일 뿐이지요. 에휴...
그래서 이부분 int형 대신 __int64형을 사용했습니다.
이 두 개의 실수 덕분에 전화면접은 패스하지 못하였습니다. ㅜ_ㅜ
그래도 서류라도 통과한게 참 다행입니다.
다른곳이었으면 서류도 통과하지 못했겠죠.
# by | 2009/04/29 23:10 | 프로그래밍수련 | 트랙백 | 덧글(4)



![[수입] Sweet Sorrow - 바이올린 소품집 (비탈리: 샤콘느 등)](http://image.aladdin.co.kr/coveretc/music/coveroff/2742436145_1.jpg)



![[수입] Andrea B...](http://image.aladdin.co.kr/coveretc/music/coveroff/2712436883_1.jpg)






☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
결과값을 먼저 구한 다음 결과값의 자리수를 구하기 때문에 사용했습니다. 사실 그렇게 하지 않아도 되기는 하지만...예시에 나온 것처럼 정확하게 하려고 했거든요.
어차피 ---갯수같은건 별로 안 중요하니 이정도만 하면 되지 않을까요;;
int main(void)
{
FILE *fp= NULL;
char op1[31]; // 첫번째 피연산자
char op2[31]; // 두번째 피연산자
char buffer[61]; // result 배열에 더해줄 버퍼
char result[61]; // 각 단계의 결과값, 마지막에는 최종 결과값이 들어감
buffer[60]= '\0';
result[60]= '\0';
if((fp= fopen("input.txt", "r"))== NULL)
{
printf("input.txt not found\n");
exit(1);
}
while(fscanf(fp, "%s %s\n", op1, op2)!= EOF)
{
if(op1[0]== '0'&& op2[0]== '0')
break;
memset(result, '0', sizeof(char)* 60);
prtSpace(strlen(op2)- 1, ' ');
printf("%s\n", op1);
prtSpace(strlen(op1)- 1, ' ');
printf("%s\n-----\n", op2);
for(int i= 0; i< strlen(op2); i++)
{
memset(buffer, '0', sizeof(char)* 60);
prtSpace(strlen(op2)- 1- i, ' ');
if(op2[strlen(op2)- i- 1]!= '0')
{
memcpy(buffer- strlen(op1)+ 60- i, op1, sizeof(char)* (strlen(op1)));
printf("%s\n", op1);
}
else
{
prtSpace(strlen(op1)- 1, '0');
printf("\n");
}
nogadaAdd(buffer, result);
}
printf("------\n%s : %ld\n", strchr(result, '1'), bufToNumber(result));
}
fclose(fp);
return 0;
}
void prtSpace(int i, char t)
{
while(i-->= 0)
printf("%c", t);
}
void nogadaAdd(char *t, char *b)
{
char carry= '0';
for(int i= 59; i>= 0; i--)
if(b[i]== t[i])
{
b[i]= carry;
carry= t[i];
}
else
b[i]= '0'+ ('1'- carry);
}
long bufToNumber(char *t)
{
long result= 0;
char *temp= strchr(t, '1');
for(int i= 0; temp[i]!= '\0'; i++)
result= result* 2+ (temp[i]- '0');
return result;
}
다만 숫자 앞의 공백이나 '-'의 개수를 맞추는 것 때문에 저렇게 복잡하게 했지요.
제가 만들었지만 참 복잡합니다. ㅡㅡ;;; 좋은 프로그램은 못되는것 같네요.
다만 .... 님의 프로그램에서는 두 개의 30자리 이진수를 받으면 오버플로우가 됩니다.
저도 그걸 생각하지 못해서...틀렸지요 ㅜㅜ