2015. 5. 2. 00:30

AlgoSpot Quiz 'ENDIANS' 문제 풀이 과정을 통한 소프트웨어 설계 연습.


▶ 소프트웨어 설계 연습 서론

소프트웨어 문제를 풀이를 할 때, "어떤 절차 혹은 방식"으로 프로그램을 작성해야 하는 것인지 감이 잘 잡히지 않는 분들이 더러 있습니다. 여러분의 머리가 나쁘기 때문에 그런 것이 아니라, 논리적으로 문제를 푸는 연습(혹은 훈련)이 부족하기 때문에 어렵게 느껴지는 것입니다.


달리기를 잘하고 싶다면, 달리기 연습부터 해야 합니다. 걷지도 못하는 아이가 뛰려고 하면 넘어질 뿐이죠. 자전거를 타고 싶다고 당장 안장에 올라타서 패달부터 밟으면 어찌 되겠습니까? 논리적으로 사고하는 능력도 훈련이 필요한 것입니다. 대한민국에서 고등학교 수준의 교육을 정상적으로 이수했다면 프로그래밍을 하는데 있어서 필요한 '학습 능력'이 부족한 것이 아닙니다. 다만, 여러분이 논리 문제를 풀어 본 적이 별로 없다는 점이 여러분의 프로그래밍을 힘들게 만드는 것입니다. 단순한 예제를 풀어가는 과정을 통해 논리로 문제를 푸는 연습을 해보고자 합니다.


 ENDIANS 문제 분석 및 논리 전개

알고스팟 왕초보 문제 "ENDIANS" 에 제시된 문제를 먼저 읽어보시기 바랍니다.


문제를 읽고 나서, 별거 아닌데? 생각하고 바로 코딩부터 하려는 분도 있을 수 있습니다. 잠시 그 손을 멈추시기 바랍니다. 운동할 때, 100 미터쯤 대충 달리는 거 문제가 아닙니다. 그런데, 왜 운동 선수들을 매일 똑같은 달리기를 반복할까요? 그저 내달리기만 하는 것은 아닐 겁니다. 멀리 오래도록 달리기 위해서는 달리는 자세와 호흡법, 페이스 조절 능력 등이 함께 필요한 법입니다.


보다 복잡하고, 어려운 문제를 풀기 위해서는 작은 문제를 풀 때도 좀 더 차분하게 접근할 필요가 있다는 얘기입니다. 저는 아래에 서술하는 내용을 일단, 종이에 적어가면서 천천히 음미하고 반추하는 시간을 가졌습니다. 그리고, 이제 키보드를 치며 다시 문서로 정리하고 그런 연후에 코드를 작성할 것입니다.  여러분도 좀 더 여유를 가지고 프로그래밍에 임하셨으면 좋겠습니다.


제 방식이 무조건 옳다라고 말씀 드리기는 어렵지만, 제가 프로그램을 작성하는데 있어 가급적 지키고 있는 절차는 다음과 같습니다.


  1. 문제 분석 (problem analysis)
    : 주어진 문제를 논리 흐름(logical flow)에 따라 다시 정리합니다. 가장 쉬운 방법은 입력, 처리, 출력 3단계로 문제를 재정의하는 것입니다.

  2. 문제 분할 (break down problem) 
    : 문제를 해결하기 쉽도록 보다 작은 문제로 쪼개 봅니다. (분할 및 정복 :devide & conquor 라고도 합니다.)

  3. 핵심 논리 구성 (composite core logic) 
    : 대다수의 문제는 쉬운 부분을 제외하고 나면, 한 두개의 어렵거나 까다로운 부분이 드러나게 됩니다.

  4. 플랫폼 문제 해결 (solving platform issue)
    : 플랫폼이라 함은 하드웨어, 운영체제, 프로그래밍 언어, 라이브러리 및 프레임워크 등 어플리케이션 프로그램을 작성하는데 있어서 의존해야 하는 모든 것을 말합니다. 풀어내고자 하는 문제를 해결하기 위해 어떤 API를 사용해야 하는지? 하드웨어 어떤 기능을 이용해야 하는지에 대한 문제를 해결합니다. 이미 알고 있는 지식이라면 쉽게 해결되지만 잘 모르거나, 처음 접하는 문제라면 적극적으로 검색 기술을 활용합니다.

  5. 코딩 (coding)
    : 이제, 코딩할 수 있습니다.!

"ENDIANS" 문제에 대한 분석 및 논리 구성 과정 사례는 아래와 같습니다.

  1. 문제 분석
    소프트웨어는 거의 "입력, 처리, 출력" 등 3단계의 흐름을 설계할 수 있습니다.

    - input         : n개의 32 bit unsigned int 값이 한 줄(line)에 하나씩 있는 텍스트 파일을 읽어들인다.
    - processing   : 엔디언 변환(endian conversion)을 수행한다.
    - output       : 변환한 숫자를 출력한다.

  2. 문제 분할
    문제를 해결하기 쉽도록 좀 더 작은 문제들(혹은 자세한 절차)로 쪼개 봅니다.

    1) 텍스트 파일을 연다.
    2) 한 줄의 텍스트 문자열을 읽어들인다.
    3) 읽어들인 문자열을 32 bit unsigned int 형 값으로 변환한다.
    4) 엔디언 변환(endian conversion)을 수행한다.
    5) 변환 결과를 출력한다.
    6) 더 이상 읽어들일 라인이 없을 경우, 파일을 닫고 프로그램을 종료한다.
    7) 아니면, 다음 행을 읽어들이기 위해 2) 단계로 이동한다.

  3. 핵심 논리 구성
    위와 같은 흐름에서 가장 중요한 (혹은 어려운) 문제는 32 bit 부호없는 정수 값의 엔디언을 변환하는 것입니다.

    1) 입력된 32 bit 부호없는 정수 값을 4 byte 배열 형태로 분리(혹은 변환)한다.
    2) 바이트 배열에 담긴 데이터를 역순(reverse order)으로 재배치 한다.
    3) 재배열된 바이트 배열을 다시 부호 없는 정수 값으로 변환한다.
    4) 변환된 정수 값을 출력한다.

  4. 플랫폼 문제 해결
    문제를 코드로 작성할 때 다양한 기술적 문제를 고려할 필요가 있습니다.

    프로그래밍 언어, 운영체제, 하드웨어, 라이브러리 등 어플리케이션 프로그램 자체에서 직접 구현하는 것이 아니라, 
    어플리케이션 하위에 위치한 (혹은 어플리케이션이 의존하고 있는) 시스템에서 제공 받는 기능들을 활용하고자 한다면, 원하는 기능이 있는지 코딩하기 전에 확인할 필요가 있고, 최악(?)의 경우에는 직접 구현해야 하는 경우도 발생합니다.

    ENDIANS 문제를 자바로 구현하고자 할 경우에 다음과 같은 문제들을 고려할 필요가 있습니다.

    텍스트 파일을 열고, 한줄 씩 읽어들이는 방법은 무엇인가?
    : java.io.File 클래스와 InputStream, BufferedReader 등 JDK에서 제공하는 API를 활용하면 됩니다.

    자바 언어에서 '부호없는 32 bit 정수' 타입을 지원하는가?
    자바는 부호 없는 정수형 타입을 지원하지 않습니다. 그러나, 64 bit 정수형인 long 타입을 사용하면 32 bit unsigned int 값을 저장할 수 있습니다.

    바이트 배열과 32 bit 정수 값을 상호 변환하는 방법은 무엇인가?
    우리에게는 구글과 StackOverflow가 있습니다. StackOverflow 참조 : 정수와 바이트 배열 간의 상호 변환 

 핵심 정리

간혹 어디까지가 알고리즘인가? 라는 의문이 들 때가 있습니다. 예를 들자면, 정수형 데이터와 바이트 배열 간에 상호 변환하는 과정도 알고리즘의 일부가 아니냐는 질문을 받습니다. 이런 과정을 직접 생각해야지 검색해서 남의 해법을 참고하는 것은 '편법' 혹은 틀린 자세는 아닌가 라는 의견을 접하고는 합니다. 소프트웨어 구현 과정의 모든 것을 스스로 해야 한다면, 파일을 읽고 쓰는 자바 API와 운영체제 까지도 직접 작성해야 하지 않을까 라는 반문을 해봅니다. 제가 정의하는 알고리즘은 '문제를 이해하고 논리적인 절차를 정의하는 것'이며, 구현 방안(방식)은 알고리즘의 본질이 아니라고 생각합니다.



Posted by 곽중선