티스토리 뷰
최종 수정: 2014-03-16
이렇게 k 번째 요소를 선택하는 문제를 해결하기 위해서는 Selection Algorithm을 사용하시는 것이 효율적입니다. 그래서 오늘 소개 드릴 내용은 Quick Selection Algorithm 을 소개 드릴려고 합니다.
Quick Selection은 이름에서도 보시다 싶이, Quick Sort의 원리를 이용하여 만든 알고리즘입니다. 정렬을 한 뒤 k 번째 요소를 찾는 것은 전체 시간 복잡도가 정렬에 따라가기 때문에 worst case 가 O (n log n) 또는 O(n^2)가 됩니다. 하지만 Quick Selection의 경우 worst case 가 O(n) 이 됩니다.
Quick Selection 동작 원리
배열 = [5, 1, 4, 3, 2]를 이용하여 2번째 작은 값을 찾는 Quick Selection 알고리즘을 살펴 보면 다음과 같습니다.
퀵 정렬과 같이 처음에 Pivot을 중간 값으로 설정합니다. (mid = 2, 배열[mid] = 4)
STEP 1.
처음에 5는 배열[mid] 보다 크기 때문에 마지막 값과 swap 합니다.
배열 = [2, 1, 4, 3, 5]
STEP 2.
다음 1은 배열[mid] 보다 크지 않기 때문에 다음으로 넘어 갑니다.
배열 = [2, 1, 4, 3, 5]
STEP 3.
다음 배열[mid]는 3보다 크기 때문에 swap합니다.
배열 = [2, 1, 3, 4, 5]
N/2 만큼의 작업으로 우리는 배열[mid] 값을 기준으로 작은 수는 왼쪽 (2, 1, 3), 큰 수는 오른쪽 (5)에 있음을 알 수 있습니다. 우리가 찾아야 하는 값은 2번째 작은 값이므로 배열 = [2, 1, 3] 을 이용하여 다시 위와 같은 작업을 하게 됩니다.
STEP 4.
배열[mid] = 1 이고 2보다 작기 때문에 다음 값을 swap 합니다.
배열 = [1, 2, 3]
이 과정을 반복하게 됩니다. 이 과정을 전체적으로 살펴보면 N번, N/2번, N/4번.... 으로 동작하여 찾게 됩니다. 따라서, 전체 시간을 T(QS) 라 가정하면 동작 시간은 다음과 같이 구할 수 있습니다.
Quick Selection, Quick Sort, Heap Sort 속도 비교
소팅 알고리즘과 비교해서 얼마나 빠른지 비교해봅시다. 비교할 알고리즘은 퀵 소트와 힙 소트 입니다. 테스트 환경은 다음과 같습니다.
데이터 셋은 4 바이트 숫자 100만개, 200만개, 300만개, ..., 1억개 까지 준비 합니다. 숫자는 0 ~ 0xFFFFFFFF 까지 랜덤 값을 넣어서 생성하도록 했습니다.
100만개 부터 1억개 까지 데이터를 생성 시 먼저 용량을 생각해보아야 합니다. 4 byte 숫자 100만개 파일 부터 4byte 숫자 1억개 파일 까지 용량을 계산하기 위해서는 먼저 100만개의 크기를 계산합니다. 100만개의 경우 4B * 1,000,000 = 4,000,000 = 3906.25 KB 의 크기는 갖습니다.
이를 계산하기 위해 초항은 3906.25 KB, n = 100, 공차(d) = 3906.25 의 등차 수열의 합을 이용하면 됩니다.
-_-;;;;;; 음... 하드에 빈 공간이 많으니 시도해보도록 하겠습니다.
아래는 데이터 셋과 용량 결과 입니다.
계산 결과랑 비슷하게 나왔군요. :D
Quick Selection 알고리즘은 다음과 같이 구현하였습니다. 모든 알고리즘은 추가적인 수정 없이 기본적인 알고리즘으로 비교를 하엿습니다. 또한 좀 더 공정하게 테스트 하기 위해 k를 N/2 로 설정하였습니다.
Quick Selection
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | unsigned int quick_selection(int n, unsigned int *ArrList, int k) { if (ArrList == NULL || n <= k) return -1; int start = 0; int end = n - 1; while (start < end) { int i = start; int j = end; int mid = ArrList[(i + j)/2]; while (i < j) { if(ArrList[i] >= mid) { int tmp = ArrList[j]; ArrList[j] = ArrList[i]; ArrList[i] = tmp; j--; } else { i++; } } if (ArrList[i] > mid) i--; if (k <= i) end = i; else start = i + 1; } return ArrList[k]; } |
Quick Sort (Wiki 의 기본 소스를 사용)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | void quick_sort (unsigned int *ArrList, int n) { if (n < 2) return; unsigned int p = ArrList[n / 2]; unsigned int *l = ArrList; unsigned int *r = ArrList + n - 1; while (l <= r) { if (*l < p) { l++; continue; } if (*r > p) { r--; continue; } unsigned int t = *l; *l++ = *r; *r-- = t; } quick_sort(ArrList, r - ArrList + 1); quick_sort(l, ArrList + n - l); } |
Heap Sort (Wiki 의 기본 소스에서 k 번째만 찾도록 수정)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | void heap_sort(unsigned int ArrList[], unsigned int cnt, int k) { unsigned int t; unsigned int n = cnt, parent = cnt/2; unsigned int index, child; while (1) { if (parent > 0) { t = ArrList[--parent]; } else { n--; if (n == cnt-k) return; t = ArrList[n]; ArrList[n] = ArrList[0]; } index = parent; child = index * 2 + 1; while (child < n) { if (child + 1 < n && ArrList[child + 1] > ArrList[child]) { child++; } if (ArrList[child] > t) { ArrList[index] = ArrList[child]; index = child; child = index * 2 + 1; } else { break; } } ArrList[index] = t; } } |
속도 비교 결과는 다음과 같습니다. (k = N/2)
X 축은 100만개 ~ 1억개 까지의 데이터이며, Y 축은 계산에 걸린 시간 (sec) 입니다.
정렬의 경우에는 전체 데이터셋을 정렬해야 하기 때문에 확실히 Selection 알고리즘이 좋은 성능을 보임을 확인 할 수 있습니다.
여기서 한 가지 재미있는 것은 Heap Sort와 Quick Sort의 관계인데요. Heap Sort의 경우, Selection 알고리즘 처럼 상위 k 번째 요소를 찾을 수 있도록 설계되어 있습니다. 그런데 왜 Quick Sort 보다 느릴까요?
이는 k 선택에 있는데 이 실험에서는 k = N/2 로 설정했기 때문에 많은 데이터를 봐야 함으로써 이런 차이가 생겼습니다.
k = 1로 설정했을 때는 다음과 같은 결과를 얻을 수 있습니다.
WOW !
힙 정렬에 놀라운 발전이 생긴것을 볼 수 있습니다. :D
이 결과를 통해 k 가 크지 않다면 힙정렬을 이용하는 것도 나쁘진 않지만 일반적으로 k-th selection 문제에서는 selection algorithm을 사용하는 것이 좋은 성능을 보임을 알 수 있습니다.
- Total
- Today
- Yesterday
- IE 10 God Mode
- IE 10 익스플로잇
- data mining
- heap spraying
- Use after free
- IE 10 Exploit Development
- 윈도우즈 익스플로잇 개발
- WinDbg
- CTF Write up
- Windows Exploit Development
- 데이터 마이닝
- 쉘 코드
- expdev 번역
- 2014 SU CTF Write UP
- shellcode writing
- IE 10 리버싱
- School CTF Write up
- UAF
- IE 11 UAF
- 힙 스프레잉
- 쉘 코드 작성
- IE UAF
- School CTF Writeup
- TenDollar CTF
- TenDollar
- Mona 2
- 2015 School CTF
- IE 11 exploit
- shellcode
- IE 11 exploit development
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |