•
프로그래밍을 배우는 사람은 알고리즘을 학습하며 많은 것을 얻을 수 있다.
◦
알고리즘 코스를 학습하며 새로운 문제를 푸는데 필요한 중요한 작업과 기술을 익힐 수 있다.
◦
외에도 프로그래밍의 좀더 일반적인 수준에서 훨씬 더 중요한 영향을 끼친다.
1. 세 가지 문제
•
최대 40억 개의 32비트 정수가 랜덤한 순서로 들어있는 순차적 파일이 주어졌다. 이때 이 파일에 포함되지 않은 임의의 32비트 정수 하나를 찾아라.
◦
메모리를 넉넉히 쓸 수 있다면 이 문제를 어떻게 풀겠는가?
◦
메모리가 수백 바이트밖에 없고 너댓 개의 외부 임시 파일을 사용할 수 있는 상황이라면 어떻게 해결할 수 있을까?
•
n개의 원소를 가지는 1차원 벡터를 i만큼 왼쪽으로 회전시켜라.
◦
예를 들어 n=8, i=3일 경우, abcdefgh라는 벡터를 회전시키면 defghabc가 된다.
◦
여분의 메모리가 수십 바이트 밖에 안 되는 상황에서도 n에 비례하는 시간 내에 벡터를 회전시킬 수 있을까?
•
주어진 영단어 사전에서 전철어구의 집합을 모두 찾아내라.
◦
예를 들어 pots, stop, tops는 철자의 순서만 바꾸면 만들어지므로 서로 전철어구이다.
2. 여기저기에 쓰이는 이진 탐색
•
프로그래밍에서 이진 탐색이 가장 일반적으로 사용되는 예는 정렬된 배열 내의 한 요소를 찾아내는 것이다.
•
그러나 이진 탐색의 응용은 단순히 정렬된 배열을 빠르게 탐색하는 것에서 그치지 않는다.
◦
우리는 앞서 첫 번째 문제를 풀기 위해 이진 탐색을 응용해볼 수 있다.
•
40억개의 숫자 중, 가장 앞의 비트가 0인 숫자와 1인 숫자를 나눈다. 그럼 그 중 더 사이즈가 하나 더 작은 쪽에 우리가 찾는 빈 숫자가 있음을 알 수 있다.
◦
해당 파일을 선택하여 이번엔 두 번째 비트를 검사한다. 이렇게 점차 더 작은 쪽의 파일을 선택해나가다 보면 빠져있는 숫자를 찾을 수 있을 것이다.
3. 기초적인 조작의 위력
•
문제 B는 n개의 원소를 가지는 벡터 x를 i만큼 왼쪽으로 회전시키고자 하는 문제였다.
•
이때 회전에 드는 시간은 n에 비례해야 하고 수십 바이트 정도의 여분 메모리만을 사용할 수 있다.
•
여기서 우리가 아는 알고 있어야 하는 점은 회전이란 결국 서로 크기가 다른 인접한 메모리 블록의 자리를 바꾸는 작업이라는 것이다.
◦
x의 처음 i개 원소를 임시 배열에 복사하고 나머지 n-i개의 원소를 i만큼 이동시킨 후, x의 뒤쪽에 임시 벡터에 있던 i개의 원소를 다시 복사하는 방법으로 회전을 구현할 수도 있다.
▪
그러나 이는 정해진 조건 안에서 문제를 풀어내기엔 적합하지 않다. 위 기법 대신 적용시켜볼만한 기법으로 저글링 기법이 있다.
◦
저글링 조작이란 x[0]을 임시 저장소인 t로 옮긴 후 x[i]를 x[0]으로 x[2i]를 x[i]로 옮기는 식의 조작을 말한다.
▪
이 조작은 t로부터 원래의 x[0]을 가져오면서 끝난다. 한 번의 조작으로 모든 원소가 옮겨지지 않았다면 x[1]을 시작으로 조작을 가한다.
◦
이 문제를 보는 관점을 바꾸면 또 다른 알고리즘을 생각해낼 수 있다.
▪
벡터 x를 회전시키는 것은 결국 벡터 ab의 두 부분을 맞바꾸어서 벡터 ba로 바꾸는 것이다.
•
a는 x의 처음 i개의 원소를 나타낸다.
▪
a의 원소의 개수가 b보다 작다고 가정하면 b를 b의 뒤에서 a만큼의 길이와 a를 교환하고 나머지를 다시 바꾸는 문제에만 집중하면 된다.
4. 정렬
•
문제 C는 수 많은 접근방법이 존재하며 이들은 아주 비효율적이고 복잡하다. 어떤 단어에 포함되어 있는 모든 문자의 순열을 고려하는 방법은 당연히 실패할 것이다.
•
이 문제는 두 개의 작은 문제로 나눠 풀어낸다. 하나는 표시를 정하는 것이고 나머지는 같은 표시의 단어를 모으는 것이다.
◦
첫 번째 문제는 정렬을 기반으로 한 표시를 사용하면 된다. 단어에 있는 문자들을 알파벳 순서로 정렬하는 것이다.
◦
두 번째 문제는 단어들을 각각의 표시 순서에 따라 정렬하여 풀 수 있다.
5. 원리
•
정렬
◦
정렬을 사용하는 것은 당연하게도 정렬된 출력을 위한 것으로 시스템의 명세일 수도 있지만 다른 프로그램의 입력을 위한 단계일 수도 있다.
◦
전철어구를 찾는 문제에서의 관심사는 정렬 자체가 아니었다. 같은 종류의 원소를 모으기 위해 정렬을 시켰을 뿐이다.
•
이진 탐색
◦
정렬된 집합에서 원하는 원소를 찾아내는 알고리즘으로 매우 효율적이고 메모리나 디스크 상에서 사용될 수 있다.
◦
유일한 단점은 전체의 집합을 미리 알고 있어야 하며 탐색에 앞서 이미 정렬된 상태여야 한다는 점이다.
•
표시
◦
하나의 동치관계가 집합을 정의하는 경우, 그 집합에 포함되는 모든 원소가 같은 표시를 가지도록 정의하는 것이 도움이 된다.
◦
단어 내의 문자를 정렬하여 하나의 전철어구 집합에 대한 표시를 만들 수 있다.
•
문제 정의
◦
이 칼럼의 주제는 문제를 정의한 다음의 과정이다. 어떤 기초적인 조작을 이용해 문제를 해결할 것인가? 새로운 간단한 조작을 정의함으로써 어렵던 문제가 쉽게 해결되는 것을 앞의 예제들을 통해 알 수 있다.
•
문제 해결자의 관점
◦
훌륭한 프로그래머는 약간 게으른 면이 있다. 그들은 처음의 아이디어를 바로 적용하려 달려들기보다는 문제를 좀더 깊게 분석하여 어떤 영감을 얻으려고 한다.
◦
물론 분석만 하면 안되며 적당한 때에 구현을 시작할 수 있는 균형이 필요하다. 그 적당한 때를 아는 것이 진정한 능력이다.