“잘하는 사람과 못하는 사람의 생산성 차이가 스무배" 이라는 말이 진지하게 나올 수 있는 분야가 컴퓨터 전공입니다. (Thinking in Java. 브루스에켈)
이런 현상의 가장 근본적인 이유는 대부분 컴퓨터 과학 교육 과정이 프로그래밍의 기술과 지식을 가르칠 뿐, 그것을 스스로 응용 할 수 있는 능력은 주지 못하기 때문입니다.
지식을 진정 자신의 것으로 만들어 활용할 수 있기 위해서는 학문이 발전하는 과정에서 일어난 발견과 깨달음을 학생 자신이 되짚어갈 수 있어야합니다.
프로그래밍은 문제 해결이다.
프로그래머가 사용하는 언어나 라이브러리, 알고리즘에 대한 지식들이 퍼즐의 조각이라면 문제 해결 능력은 적재 적소에 퍼즐 조각을 배치하고 이들을 연결해서 큰 그림을 만드는 능력이라고 할 수 있습니다.
02. 문제 해결 개관
파인만 알고리즘
- 칠판에 문제를 적는다.
- 골똘히 생각한다.
- 칠판에 답을 적는다.
문제 해결과정을 단계별로 나누는 것은 별 것 아닌것 같지만 매우 강력합니다. 첫번째에서 문제를 다시적는 다는 것은 문제를 읽고 이해한뒤 자신의 언어로 재정의 하는 것을 말합니다.
How to solve it 이라는 책에서는 문제 해결 과정을 다음과 같이 네가지 단계로 나눠서 소개합니다.
- 문제를 이해한다.
- 어떻게 풀지 계획을 세운다.
- 계획을 수행해서 문제를 해결한다.
- 어떻게 풀었는지 돌아보고, 개선할 방법이 있는지 찾아본다.
이 단계에서 좀더 개선해봅시다.
- 문제를 읽고 이해한다.
- 문제를 익숙한 용어로 재정의한다.
- 어떻게 해결할지 계획을 세운다.
- 계획을 검증한다.
- 프로그램으로 구현한다.
- 어떻게 풀었는지 돌아보고, 개선할 방법이 있는지 찾아본다.
문제를 읽고 이해하기
이 단계의 중요성은 아무리 강조해도 지나치지 않습니다. 문제를 곁눈질하고 딸려오는 그림과 입출력 예제를 보고 문제가 원하는것을 유추하기 십상이다. 하지만 이와 같은 성급한 행동은 하지않는게 좋습니다. 문제의 설명을 공격적으로 읽으며 문제가 원하는 바를 완전히 이해하는 과정이 반드시 필요합니다. 문제의 궁극적인 목적을 옳게 이해하더라도 사소한 제약 조건을 잘못 이해하면 풀 수 없게 되는 문제들이 흔하기 때문입니다.
재정의와 추상화
문제 해결 알고리즘의 두번째 단계는 자신이 다루기 쉬운 개념을 이용해서 문제를 자신의 언어로 풀어쓰는 것입니다. 이 과정은 문제가 요구하는 바를 직접적으로 이해하기 위해 꼭 필요하며, 요구하는 바가 복잡한 문제일수록 그 중요성이 더해집니다.
이 과정에서 또 하나 중요한 개념이 있는데 바로 문제의 추상화입니다. 추상화란 현실 세계 개념을 우리가 다루기 쉬운 개념으로 옮겨 표현하는 과정입니다. 문제의 본질을 어떤 방식으로 재구성하느냐에 따라 같은 일을 하는 프로그램이라도 전혀 다른 문제로 받아들여질 수 있습니다.
어떤 부분을 추상화할 것인지를 선택하는 작업과 문제를 재정의하는 방법들에 대한 고찰은 좋은 프로그래머가 되기 위해 필수적인 과정입니다.
계획 세우기
문제 해결의 다음 단계는 문제를 어떻게 해결할지 계획을 세우는 것입니다.
이 과정에서 문제는 어떤 방식으로 해결할지 결정하고, 사용할 알고리즘과 자료구조를 선택합니다. 사실상 이 부분이 문제 해결의 가장 중요한 단계라고 할 수 있습니다. 물론 자신이 알고있는 알고리즘이나 자료 구조를 직접 적용할 수 있는 문제라면 이 과정은 너무나도 간단합니다.
계획 검증하기
계획을 세웠다고 해서 곧장 키보드를 잡을 수 있는 것은 아닙니다. 구현을 시작하기 전에 계획을 검증하는 과정을 거쳐야 합니다. 이 과정에서는 우리가 설계한 알고리즘이 모든 경우에 요구 조건을 정확히 수행하는지를 증명하고 수행에 걸리는 시간과 사용하는 메모리가 문제의 제한 내에 들어가는지 확인해야합니다.
계획 수행하기
구현 과정은 아무리 강조해도 지나치지 않습니다. 계획을 잘세워도 프로그램의 구현이 부정확하거나 비효율적이면 프로그램은 정확히 동작하지 않습니다.
회고하기
문제 해결에 당장 직접적인 영향은 없지만 장기적으로 가장 큰 영향을 미치는 단계가 바로 회고입니다. 회고란 자신이 문제를 해결한 과정을 돌이켜보고 개선하는 과정을 말합니다. 효과적으로 회고를 수행하는 가장 좋은 방법은 문제를 풀 때 마다 코드와 함께 자신의 경험을 기록으로 남기는 것입니다. 이때 문제의 간단한 해법과 함께 자신이 어떤 방식으로 접근했는지, 그리고 문제의 해법을 찾는 데 결정적이었던 깨달음은 무엇이었는지를 기록하면 좋습니다. 나중에 읽어내려가면서 그때 뭘 배웠는지 되새길 때도 유용하고, 기록하다 보면 문제들 간에 공통으로 필요한 통찰들이 패턴화되기도 합니다.
반대로 한번에 맞추지 못한 경우에는 오답 원인도 꼭 적어두는 것이 좋습니다. 과거의 실수에서 배우기란 매우 어렵기 때문에 대개 한 실수를 반복하게 되는데, 오답 노트를 적다보면 자신이 자주 틀리는 부분을 알게되고 결과적으로 실수를 줄일 수 있습니다.
처음보는 기술이나 접근을 한번만에 자신의 것으로 하기란 힘들지만 두번, 세번 해당 기법을 이용해 문제를 풀다 보면 비슷한 문제를 봤을 때 해당 기법이 떠오르는 때가 올것입니다.
경험을 통해서 위과정을 따로 의식하지 않아도 수행할 수 있도록 하자!!!
2.3 문제 해결 전략
어려운 문제일수록 다양한 방법을 시도해 보면서 답을 찾아야합니다.
문제 해결 전략에서 가장 강조해야 할 것은 문제와 답의 구조에 대한 직관의 중요성입니다. 직관은 먼저 해당 문제를 해결하는 알고리즘이 대략적으로 어떤 형태를 가질지를 짐작할 수 있게 해줍니다.
막막한 문제는 어떻게 해결할까요? 그냥 아이디어가 떠오르길 기대하면서 멍하니 앉아 있을수도 있지만, 좀더 체계적으로 백지에서부터 시작해 문제를 해결하기 위한 기반을 차근차근 쌓아올리면서 점진적으로 전진하면 좋습니다.
체계적인 접근을 위한 질문들
비슷한 문제를 풀어본 적이 있던가?
형태가 비슷하거나 관련된 문제를 풀어본 적이 있다면 이전에 적용했던 방법과 비슷한 접근 방법을 사용할 거라고 예측할 수 있습니다. 기존에 접했던 문제가 온전히 경험이 되려면 그 원리를 완전히 이해하고 변형할 수 있어야합니다.
내가 문제를 푸는 과정을 수식화할 수 있을까?
손으로 입력에 대한 예제를 직접 해결해 보는것. 자신이 문제를 해결한 과정을 공식화해서 답을 만든느 알고리즘을 만들 수 있는 경우가 흔히있고, 그렇지 못하더라도 이 과정에서 알고리즘이 어떤 점을 고려해야하는지를 알게됩니다.
문제를 단순화 할 수 없을까?
문제를 좀더 쉬운 변형판을 먼저 풀어보는 것입니다. 문제의 제약조건을 없애거나, 계산해야 하는 변수의 수를 줄이거나, 다차원문제를 1차원으로 줄여서 표현할 수도 있습니다. 이때 단순화된 문제의 해법이 원래 문제의 해법에 대한 직관을 제공할 수도 있고, 이것을 직접적으로 이용해 원래 문제를 풀어낼 수도 있습니다.
그림으로 그려볼 수 있을까?
문제의 해법에 대한 직관을 얻는 또 다른 방법은 문제에 관련된 그림을 그려보는 것입니다. 많은 사람들의 사고체계는 숫자의 나열보다 기하학적 도형을 더 직관적으로 받아들이기 때문입니다.
수식으로 표현할 수 있을까?
가능한 한 직관을 얻기 좋은 방향으로 사고를 전개하는 다른 접근 방식과는 반대로, 평문으로 쓰여있는 문제를 수식으로 표현하는 것도 도움이 되는 경우가 있습니다. 수식을 전개하거나 축약하는 등 순수한 수학적 조작이 문제를 해결하는데 큰 도움을 줄 수도 있기 때문입니다.
문제를 분해할 수 있을까?
더 다루기 쉬운 형태로 문제를 변형하는 것. 이 방법은 문제에 주어진 복잡한 조건을 더 단순한 형태를 갖는 조건의 집합으로 분해합니다. 한개의 복잡한 조건보다 여러개의 단순한 조건이 다루기 쉽기때문에 이 변형은 종종 유용합니다.
뒤에서부터 생각해서 문제를 풀 수 있을까?
또 다른 유용한 문제 변형 기법은 문제에 내재된 순서를 바꿔보는 것입니다.
순서를 강제할 수 있을까?
특정 형태의 답만을 고려할 수 있을까?
순서를 강제하는 기법의 연장선으로 정규화기법이있습니다. 정규화란 우리가 고려해야할 답들 중 형태가 다르지만 결과적으로는 똑같은 것들을 그룹으로 묶은 뒤, 각 그룹의 대표들만을 고려하는 방법입니다.