사이냅소프트 채용퀴즈 4번

Q4 – 이진수 곱하기
두개의 이진수에 대한 곱하기 프로그램을 작성하라.
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)

트랙백 주소 : http://p5ydhcw.egloos.com/tb/4914806
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by 문제를 at 2009/05/04 17:52
어렵게 푸셨네요 ㅎㅎ 30자리 이하의 수 라고했으면 이미 대부분 예외처리는 해결 되었을 텐데 __int64까지 쓰셨군요;;;
Commented by 퓨리넬 at 2009/05/05 00:00
30자리의 이진수 두 개를 곱하면 32비트로는 오버플로우니까요 ㅎㅎ
결과값을 먼저 구한 다음 결과값의 자리수를 구하기 때문에 사용했습니다. 사실 그렇게 하지 않아도 되기는 하지만...예시에 나온 것처럼 정확하게 하려고 했거든요.
Commented by .... at 2009/05/21 02:34
지나가다 눈에 띄어서;;.
어차피 ---갯수같은건 별로 안 중요하니 이정도만 하면 되지 않을까요;;

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;
}
Commented by 퓨리넬 at 2009/05/29 17:00
네. 사실 간단하게 하려고 하면 꽤나 간단하게 할 수 있습니다.
다만 숫자 앞의 공백이나 '-'의 개수를 맞추는 것 때문에 저렇게 복잡하게 했지요.
제가 만들었지만 참 복잡합니다. ㅡㅡ;;; 좋은 프로그램은 못되는것 같네요.

다만 .... 님의 프로그램에서는 두 개의 30자리 이진수를 받으면 오버플로우가 됩니다.
저도 그걸 생각하지 못해서...틀렸지요 ㅜㅜ

:         :

:

비공개 덧글

◀ 이전 페이지 다음 페이지 ▶