칼럼 1: 조개 껍질 깨기
1.1 대화
Q: "디스크 파일을 어떻게 정리하지?"
A: "오픈 소스 사이트에 있는 디스크 파일 정리 라이브러리를 사용해봐"
정확한 문제에 대한 요구사항 분석이 없다면 최적의 해답을 이끌어낼 수 없다.
1.2 정확한 문제 기술
대화를 통해 분석된 문제의 정보
- 데이터의 종류: (지역 번호 3자리 +) 7자리의 전화번호 숫자
- 지역번호가 800으로 시작하는 무료통화 번호가 데이터
- 전화번호는 겹칠 수 없음
- 시스템에서 해당 문제를 해결하기 위해 할당할 수 있는 메모리 공간은 1MB 남짓임
정리된 문제 정보
입력: 최대 n개의 양의 정수를 포함하는 파일로, 각 숫자는 n보다 작고, n=107이다.
어떤 숫자가 두 번 이상 나오는 것은 치명적 에러이다.
정수 이외의 데이터는 없다.
출력: 입력된 정수를 오름차순으로 정렬한 리스트
제약조건: 메모리를 많아야 대략 1MB 정도를 사용할 수 있고,
디스크 공간은 충분하다.
실행 시간은 최대 몇 분 정도가 될 수 있고, 10초 정도 안에 작업을 끝낼 수 있으면 충분함.
1.3 프로그램 디자인
제안할 수 있는 방법
1.4 구현 스케치
20 이하의 음이 아닌 정수는 20비트로 표현 가능함.
ex) {1, 2, 3, 5, 8, 13} 을 비트열로 나타내는 방법
이 것을 전화번호부에 적용하면 다음과 같다.
(주의! 위의 데이터 형식은 아직도 1MB를 초과하는 메모리를 사용함. - 약 1.25MB)
위와 같은 데이터 표현의 정렬은 해당 데이터의 특성을 이용한다.1.1 대화
Q: "디스크 파일을 어떻게 정리하지?"
A: "오픈 소스 사이트에 있는 디스크 파일 정리 라이브러리를 사용해봐"
정확한 문제에 대한 요구사항 분석이 없다면 최적의 해답을 이끌어낼 수 없다.
1.2 정확한 문제 기술
대화를 통해 분석된 문제의 정보
- 데이터의 종류: (지역 번호 3자리 +) 7자리의 전화번호 숫자
- 지역번호가 800으로 시작하는 무료통화 번호가 데이터
- 전화번호는 겹칠 수 없음
- 시스템에서 해당 문제를 해결하기 위해 할당할 수 있는 메모리 공간은 1MB 남짓임
정리된 문제 정보
입력: 최대 n개의 양의 정수를 포함하는 파일로, 각 숫자는 n보다 작고, n=107이다.
어떤 숫자가 두 번 이상 나오는 것은 치명적 에러이다.
정수 이외의 데이터는 없다.
출력: 입력된 정수를 오름차순으로 정렬한 리스트
제약조건: 메모리를 많아야 대략 1MB 정도를 사용할 수 있고,
디스크 공간은 충분하다.
실행 시간은 최대 몇 분 정도가 될 수 있고, 10초 정도 안에 작업을 끝낼 수 있으면 충분함.
1.3 프로그램 디자인
제안할 수 있는 방법
- 디스크 기반의 머지 정렬
- 다중 패스 정렬
- 만약 각 숫자를 7바이트에 저장한다면,
143,000 개 = 1MB / 1Byte * 7 자리 - 각 숫자를 따로 표현
250,000 개 = 1MB / 32bit (4Byte) - 전화번호 전체를 하나의 정수로 표현 - 데이터를 40번으로 나누어 읽어서 메모리에서 Quick sort를 수행한다.
- 메모리에 데이터를 모두 로드하여, 한번에 정렬하는 '놀라운' 방법?
1.4 구현 스케치
20 이하의 음이 아닌 정수는 20비트로 표현 가능함.
ex) {1, 2, 3, 5, 8, 13} 을 비트열로 나타내는 방법
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
이 것을 전화번호부에 적용하면 다음과 같다.
| 0000000 | 0000001 | 0000002 | ... | 1352311 | ... | 9999999 |
| 1(있음) | 0(없음) | 1(있음) | 1(있음) | 0(없음) |
(주의! 위의 데이터 형식은 아직도 1MB를 초과하는 메모리를 사용함. - 약 1.25MB)
- 상대적으로 작은 범위 내의 입력
- 중복된 숫자가 없음
- 각 레코드 정수 이외의 다른 연관된 데이터가 없음.
솔루션
- 1단계: 비트맵을 모두 0으로 초기화
for i = [0, n)bit[i] = 0 - 2단계: 존재하는 숫자를 비트맵에 표시
for each i in the input filebit[i] = 1 - 3단계; 정렬된 결과를 기록
for i = [0, n)if bit[i] == 1write i on the output file
1.5 원리
- 작은 문제에 대한 주의 깊은 분석으로 때로는 엄청난 실질적 이득을 얻을 수 있다.
- 그러나, 이 프로그램의 특화된 구조는 특정 부분의 명세가 변경될 경우 수정하기 어려울 것
(ex. 전화번호 수가 8자리로 늘어난다던지) - 정확한 문제 정의는 문제를 해결하는 데 있어 큰 몫을 차지한다.
- 비트맵 데이터 구조
- 이 데이터 구조는 원소가 중복되지 않고 원소와 관련된 다른 데이터도 없는 촘ㅊ모한 유한 집합을 표현할 수 있음.
- 이 비트맵 데이터는 더 복잡ㅎ나 엔트리를 갖는 테이블에 대한 인덱스로 사용할 수 있음.
- 다중 패스 알고리즘
- 입력 파일을 여러 번 읽어 각 단계마다 조금씩 작업하는 방식에 응용할 수 있음.
- 시간-공간의 트레이드 오프(Tradeoff)인 것과 아닌 것
- 일반적으로 프로그래밍 분야에는 시간-공간 트레이드오프가 많음.
- ex) 2패스 알고리즘은 실행 시간을 두 배로 늘리는 대신 사용공간을 절반으로 줄임
- 하지만 저자 경험 상, 프로그램이 사용하는 공간을 줄이면 실행시간 역시 줄어드는 경우가 자주 있음.
- 이유 1: 데이터가 적다는 것은 그것을 처리하는 데 걸리는 시간도 적다는 것을 의미함
- 이유 2: 데이터를 디스크보다 메모리에 두면 디스크에 접근하는 오버헤드를 피할 수 있음.
- 두 가지 모두를 개선할 수 있는 문제라면, 그것은 그 문제에 대한 솔루션 디자인이 최적화되지 않았다는 증거!
1.6 연습 문제
- 메모리가 충분하다면, 집합(set)의 표현과 정렬을 어떻게 구현하겠는가? (라이브러리 사용)
- 비트 벡터는 비트 연산(AND, OR, SHIFT 같은)을 이용해 어떻게 구현할 수 있는가?
- 문제에서 제공된 시스템이 단 0.25MB의 추가 여유공간도 없다면, 어떻게 알고리즘을 변경해야 하는가?
- 무료통화 지역번호가 기존 800 단일 번호에서 877, 888 등 다른 국번도 생긴다면, 이 프로그램은 어떻게 변경되어야 하는가?
- 유인 우주비행 개척자들은 곧 우주의 극한 환경에서도 잘 동작하는 필기도구의 필요성을 깨달았다.
전해오는 이야기에 따르면 NASA는 이 문제를 해결하기 위해 백만 달러의 연구비를 들여 특별한 페을 개발했다고 한다.
러시아는 같은 문제를 어떻게 해결했을까?