[C++] 심화프로그래밍 Week2

Yoons·2022년 9월 15일
0
post-thumbnail

실습 보고서 작성 요령


문제분석

  • 결과 화면을 캡쳐 하여 첨부, 해당 결과가 도출된 이유와 타당성 분석

프로그램 설계 및 알고리즘

  • 해결 방법에 따라 프로그램 설계 및 알고리즘 등 기술
  • e.g.) 문제 해결 과정 및 핵심 알고리즘 기술

소스코드 및 주석

  • 소스코드와 그에 해당하는 주석 첨부
  • 각각의 함수가 수행하는 작업, 매개변수, 반환 값 등을 명시
  • 소스코드 전체 첨부(소스코드 화면 캡처X, 소스코드는 복사/붙여넣기로 첨부)

결과 및 결과 분석

  • 결과 화면을 캡쳐 하여 첨부, 해당 결과가 도출된 이유와 타당성 분석

소감

  • 실습 문제를 통해 습득할 수 있었던 지식, 느낀 점 등을 기술

Prob 1


문제분석

유클리드 호제법은 두 개의 자연수를 받아 최대공약수를 구하는 알고리즘 중에 하나이다. 유클리드 호제법을 이용해 최대공약수를 구하는 방법은 2개의 자연수 a, b에 대해서 a를 b로 나눈 나머지를 r이라 하면(단, a>b), a와 b의 최대공약수는 b와 r의 최대공약수와 같음을 이용한다. 이 성질을 이용하여 b를 r로 나눈 나머지 r'를 구하고, 다시 r을 r'로 나눈 나머지를 구하는 과정을 반복하여 나머지가 0이 되었을 때 나누는 수가 a와 b의 최대공약수이다. 최소공배수는 최대공약수를 구함으로써 계산할 수 있다. 최소공배수는 두 자연수 a, b가 주어졌을 때 a와 b를 곱한 값을 최대공약수로 나눠주면 된다. 과제를 해결하기 위해 cin으로 두 자연수를 입력 받고 최대공약수를 구하기 위해 함수 gcd를 정의한다. gcd 함수 안에는 a와 b를 나눈 나머지가 0이 될 때까지 계산하도록 반복문을 사용한다. 최대공약수를 구했다면 이를 이용해 최소공배수를 구하고 cout을 이용해 출력한다.

프로그램 설계 및 알고리즘

  1. gcd 함수를 선언한다. 이때 매개변수로 두 개의 숫자를 받는다.
  2. (gcd function) 유클리드 호제법에 따라 두 수를 나누기 전 대소비교를 통해 변수 값을 서로 바꿔준다. 이때 값을 일시적으로 저장할 수 있는 temp 변수를 선언한다.
  3. (gcd function) 대소비교를 통해 올바르게 변수 값을 서로 바꿔 주었다면 반복문을 통해 나눗셈을 해준다. 문제분석에서 확인했듯이 a와 b를 나눈 나머지를 r이라고 할 때(단, a>b) 다음 계산에서는 b를 a의 위치로, r을 b의 위치로 옮겨 나누는 과정을 반복해야함으로 값을 일시적으로 저장할 수 있는 temp2라는 변수를 선언한다. while문 절차에 따라 최종적으로 b가 0이 될 때 a의 값이 최대공약수가 되므로 결과값을 반환한다.
  4. (main function) cin으로 두 개의 숫자를 받는다.
  5. (main function) 앞에서 정의한 gcd 함수에 입력 받은 두 개의 숫자를 넘겨주고 반환한 결과를 gcd_result라는 변수에 저장한다.
  6. (main function) 최소공배수를 구하기 위해 최대공약수인 gcd_result를 이용한다. 입력 받은 두 수를 곱한 값을 gcd_result로 나눈 후 결과값을 lcm_result에 저장한다.
  7. 최대공약수와 최소공배수가 저장되어 있는 변수 gcd_result와 lcm_result를 cout을 이용해 출력한다.

소스코드 및 주석

#include <iostream>
using namespace std;

int gcd(int a, int b){ // a와 b를 매개변수로 하는 gcd 함수 선언
    int temp; // 대소비교 후 변수 값을 서로 바꿔주는데 사용될 저장소 temp 변수 선언

    if (a < b){ // a가 b보다 작다면
        temp = a; // a 값을 temp 변수에 일시 저장
        a = b; // b 값을 a에 저장
        b = temp; // a의 값이 담겨있는 temp 값을 b에 저장
    }
    
    int temp2; // 변수 값을 서로 바꿔주는데 사용될 저장소 temp2 변수 선언

    while (b != 0){ // b가 0이 될 때까지 반복
        temp2 = a%b; // a와 b를 나눈 나머지 값을 temp2 변수에 일시 저장
        a = b; // b 값을 a에 저장
        b = temp2; // a의 값이 담겨 있는 temp2 값을 b에 저장
    }
    return a; // 최대공약수 a 반환
}

int main(){
    int a, b; // 입력 변수 선언

    cout << "두 수 입력 : ";
    cin >> a >> b; // 숫자 연달아 입력

    int gcd_result, lcm_result; // 결과값 변수 선언
    gcd_result = gcd(a, b); // gcd 함수를 호출해 계산 후 반환값을 gcd_result에 저장
    lcm_result = (a*b)/gcd_result; // gcd_result를 이용해 lcm_result 계산

    cout << "최대공약수 : " << gcd_result << endl; // 최대공약수 결과값 출력
    cout << "최소공배수 : " << lcm_result << endl; // 최소공배수 결과값 출력
}

결과 및 결과 분석


두 수를 연달아 입력 받았는데, spacebar로 구분했을 때 올바르게 입력되는 것을 확인할 수 있다. 또한 최대공약수와 최소공배수가 올바르게 계산되어 출력되는 것도 확인할 수 있었다.

소감

최대공약수와 최소공배수가 무엇을 의미하고 수학적으로 어떻게 구하는지는 알고 있었지만, 코드를 통해 논리적으로 이해하고 구현하는 것이 쉽지 않았다. 특히 최대공약수를 구하는 과정에서 나머지가 있다면 그 값을 다음 단계에서는 왜 분모값으로 가는 것인지 이해하지 못했다. 하지만 최대공약수가 '공통된 약수 중 가장 큰 값', 즉 '약수'라는 점을 생각했을 때 결국 나머지가 0이 되어야 분자값의 약수에 해당한다는 알고리즘을 이해하고 나서 비로소 코드를 작성할 수 있었다. 문제를 처음 마주했을 때 "왜 처리조건에 gcd 함수만 있을까?"라는 의문을 가졌다. 하지만 최대공약수를 이용해 최소공배수를 구하는 과정을 이해하고 나니 굳이 최소공배수를 구하는 함수를 만들지 않아도 된다는 점을 깨달았다.


Prob 2


문제분석

덧셈(+), 뺄셈(-), 곱셈(), 나눗셈몫(/), 나눗셈나머지(%)의 정수 5칙 연산이 가능한 프로그램 작성 문항이다. 먼저, cin을 이용해 a + b의 형식으로 입력 받는다. 여기에서 정수와 연산자는 spacebar로 분리하라는 조건이 있는데, 그 이유는 cin으로 2개의 숫자와 하나의 연산자를 한번에 받으려면 spacebar로 분리해야하기 때문일 것이라고 예상한다. 반복문과 조건문을 사용해야 하므로 숫가 2개와 하나의 연산자가 올바르게 입력되었다면 무한 반복하도록 작성한다. 그 안에서 +, -, , /, %에 대해 조건문을 작성해 각각의 연산자에 해당하는 계산을 수행한다. 최종적으로 cout을 통해 계산 결과를 출력한다.

프로그램 설계 및 알고리즘

  1. 반복문을 제어하는 데 사용될 int형 변수 i, int형 숫자 a, b, result와 연산자를 저장할 char형 변수 opt를 선언한다.
  2. while문을 이용해 loop를 생성한다.
  3. while문 안에 if, else if, else 조건문을 사용하여 각각의 연산자 또는 다른 문자가 왔을 때의 경우로 나눈다.
  4. 연산자에 따라 계산을 다르게 하여 결과를 result에 저장하고, 올바르지 않은 연산자가 입력된 경우 에러메세지를 출력함과 동시에 break를 이용해 loop를 빠져나간다.
  5. 'a ? b = c'의 형식으로 결과를 출력하고 반복 제어를 위해 1회 반복을 마치면 i를 1씩 증가시킨다.

소스코드 및 주석

#include <iostream>
using namespace std;

int main(){
    int i = 0, a, b, result; // 변수 선언
    char opt; // 연산자를 저장할 char형 변수 선언

    while (i < 6){ // n회 반복
        cin >> a >> opt >> b; // 숫자 a, 연산자 opt, 숫자 b를 순서대로 입력. 구분은 spacebar

        if (opt == '+'){ // 연산자가 '+'라면
            result = a + b; // a + b 값을 result에 저장
        }
        else if (opt == '-'){ // 연산자가 '+'라면
            result = a - b; // a - b 값을 result에 저장
        }
        else if (opt == '*'){ // 연산자가 '+'라면
            result = a * b; // a * b 값을 result에 저장
        }
        else if (opt == '/'){ // 연산자가 '+'라면
            result = a / b; // a / b 값을 result에 저장
        }
        else if (opt == '%'){ // 연산자가 '+'라면
            result = a % b; // a % b 값을 result에 저장
        }
        else { // opt에 다른 문자가 입력된다면
            cout << "ERROR : Operator disabled. End a loop." << endl; // 에러메세지 출력
            break; // 반복 종료
        }
        cout << a << ' ' <<  opt << ' ' <<  b << " = " << result << endl; // 'a + b = c' 형식으로 결과 출력
        i++; // 반복 1회 당 i를 +1
    }
}

결과 및 결과 분석


조건에 맞춰 올바르게 입력하면 모든 연산자에 대해 올바르게 계산되는 것을 확인할 수 있다. 특히 음수나 복잡한 계산도 잘 해내는 것을 볼 수 있다. 음수에 대한 계산은 spacebar로 구분하지 않으면 이게 음수인지, '-'연산자인지 모호하므로 주의해야할 것이다. 또 정의되지 않은 연산자가 입력되었을 때 에러메세지를 출력하고 loop를 잘 빠져나간 것도 확인할 수 있다.

소감

처음 시도할 때는 먼저 a, b, opt를 입력 받고 그 값이 NULL이 아니라면 무한루프를 돌도록 계획했다. 하지만 cin을 먼저 작성하고 그 다음 반복문(조건)을 작성하다보니 결과가 출력된 다음 a, b, opt를 다시 입력받는게 아니라 결과가 무한 출력되었다. 그 아유는 cin이 반복문 밖에 있었기 때문이다. 이 때문에 첫 번째 결과 출력 후 변수 값을 다시 입력 받지 않았다. 이 문제를 해결하기 위해 cin을 반복문 안에 위치하게 했는데, while문의 조건을 바꿔야 하는 문제가 발생했다. 왜냐하면 처음에는 while문에 들어가기 전 a, b, opt값을 입력 받았지만, while문 안에 넣게 되면 a, b, opt를 변수로 while문 조건을 작성할 수 없다. 따라서 while문을 제어할 변수 i를 새로 선언함으로써 간단하게 문제를 해결했다. 원래의 계획과는 조금 틀어졌지만, 원하는대로 결과가 출력되어 다행이라고 생각한다.


Prob 3


문제분석

문자열을 입력 받고 그것을 반대로 출력하는 프로그램 작성 문항이다. getline은 공백을 포함하여 모든 문자열을 저장한다. 문자열을 먼저 입력 받고 만큼 (0 + n)번째 index에 저장된 문자와 (k - n)번째 index에 저장된 문자를 서로 바꾸는 것을 (문자열의 길이/2)번 반복하는 반복문을 작성한다(k는 마지막 index). 반복문을 모두 마치면 cout을 이용해 결과를 출력한다.

프로그램 설계 및 알고리즘

  1. header file을 전처리하여 string형 변수 text, 반복문 제어 변수 i, 문자열 일시 저장 변수 temp, 문자열 길이 변수 len을 선언한다.
  2. getline으로 문자열을 입력 받아 text에 저장한다.
  3. 입력 받은 문자열의 길이를 계산하여 변수 len에 저장한다. 문자열의 길이는 문자열.length()를 이용한다.
  4. 데칼코마니처럼 (0 + n)번째 index에 저장된 문자와 (k - n)번째 index에 저장된 문자를 서로 바꾸는 것 (문자열 길이/2)번 반복하는 반복문을 작성한다. 이때 문자열의 가장 마지막 index는 len-1임에 주의한다.
  5. cout을 이용해 결과를 출력한다.

소스코드 및 주석

#include <iostream>
#include <string> // <string> header file 전처리
using namespace std;

int main(){
    string text; // string형 text 변수 선언
    int i, temp, len; // 반복문 제어 변수 i, 문자열 일시 저장 변수 temp, 문자열 길이 변수 len 선언

    cout << "문자열을 입력하세요 : ";
    getline(cin, text); // 문자열을 입력 받아 text 변수에 저장

    len = text.length(); // 반복문에 사용 될 입력 받은 문자열의 길이 계산

    for (i = 0; i < len/2; i++){ // (문자열 길이/2)회 반복
        temp = text[i]; // i번째 index에 저장된 문자를 temp에 저장
        text[i] = text[len-1-i]; // (len-1-i)번째 index에 저장된 문자를 i번째 index에 저장
        text[len-1-i] = temp; // temp에 저장된 문자를 (len-1-i)번째 index에 저장
    }

    cout << "반대로 출력 : ";
    cout << text << endl; // 결과 출력
}

결과 및 결과 분석


getline을 통해 공백, 특수문자를 포함하여 모든 문자가 잘 입력되었다. 또한 반복문을 통해 올바르게 문자를 반대로 하는 것도 결과를 통해 확인할 수 있다.

소감

1주차 1번 문항에서와 마찬가지로 string 헤더파일을 이용해 string 변수를 선언하고, getline을 이용해 긴 문자열을 한번에 입력 받았다. 문제를 처음 읽었을 때 문자열을 저장할 배열을 선언해야하나 했지만, string 변수는 저장된 문자의 길이만큼 배열로 저장하기 때문에 따로 배열을 선언해 줄 필요가 없어서 편했다. 반복문을 통해 문자를 서로 바꾸는 것은 '데칼코마니'를 떠올리면 편하다. 데칼코마니를 만들기 위해 종이를 가로 방향으로 접으면 가로의 길이는 1/2이 된다. 종이의 가로를 일정하게 나뉜 0-9 구간이 있다고 생각해보면, 0구간은 9구간과, 1구간은 8구간과, 2구간은 7구간..과 맞물린다고 이해할 수 있다. 이와 같은 알고리즘을 이해하고 적용하여 다소 쉽게 해결했다. 이번 문항에서 가장 크게 느낀 것은 string의 강력함이었다.


참고 Reference

profile
모든 글은 저의 눈물을 머금으며 작성한 글이니..재밌게 봐주세요 :) 깃헙 맞팔@

0개의 댓글