보간 탐색 (Interpolation Search)
보간 탐색 (Interpolation Search)
- 균일하게 n개의 데이터가 분포되어 정렬된 배열이 있을 경우 이진 탐색보다 성능이 좋음
ex) 10, 12, 13, 15, 18, 22, 25, 26, 27, 30, ... => 성능 ↑
10, 102, 192, 375, 462, 1777, ... => 성능 ↓ - 이진 탐색은 탐색 대상의 값과 상관없이 무조건 절반씩 잘라서 비교하지만, 보간 탐색은 탐색 대상이 앞쪽에 있을 경우 앞쪽부터 탐색을 진행
- 선형 보간법을 이용해 탐색 위치를 결정함
보간 탐색 방법
- 탐색을 시작할 위치를 공식을 통해 알아낸다.
- 해당 위치에 탐색 대상이 있을 경우 해당 위치 값을 반환한다.
- 해당 위치의 값이 탐색 대상보다 작으면 탐색 대상의 왼쪽을 다시 탐색하고, 크다면 오른쪽을 탐색한다.
- 탐색 대상을 찾거나 탐색범위가 0이 될 때까지 1~3 과정을 반복한다.
- 탐색 위치 지정 공식
직선의 방정식 : \(y = mx + c\) 를 이용 (y : 배열의 값, x : 배열 값의 인덱스, m : 기울기)
low, high, a를 방정식에 넣어서 계산
\(arr[high] = m * high + c\)
\(arr[low] = m * low + c\)
\(arr[a] = m * a + c\)
두 점의 좌표를 알면 기울기를 구할 수 있음
\((low, arr[low]), (high, arr[high])\) 일 때, 기울기 : \(\frac{arr[high]-arr[low]}{high-low}\)
\(arr[a] = m * a + c\) 수식에서 c를 제외하고 왼쪽으로 옮기면 아래와 같이 수식이 된다.
\(c = arr[a] - m * a\)
찾고자 하는 인덱스는 음수가 나올 수 없으므로 이를 \(arr[low] = m * low + c\)에 대입한다.
\(arr[low] = m * low + arr[a] - m * a\)
\(m * a = m * low + arr[a] - arr[low]\)
\(a = low + \frac{(arr[a] - arr[low])}{m}\)
m(기울기)은 위에서 구했으므로 대입하면 최종적으로 아래와 같이 수식이 나온다.
$$a = low + \frac{(arr[a] - arr[low])}{(arr[high]-arr[low])} * (high - low)$$
arr[a] : 탐색 대상
a : 찾고자하는 인덱스
구현
- 기본 구현
[InterpolationBasic.java]
package Search.Interpolation; public class InterpolationBasic { public static int search(int[] arr, int start, int end, int target) throws Exception { // 찾고자하는 값의 인덱스 int targetIndex = 0; // 재귀 탈출 조건 // 재귀가 계속해서 호출될수록 아래 targetIndex - 1 또는 targetIndex + 1에 의해 // 값이 배열의 범위에서 벗어날 수 있음 if (arr[start] > target || arr[end] < target) { throw new Exception("데이터를 찾을 수 없습니다."); } // 데이터의 모든 값이 중복된 경우 (10, 10, 10, 10, 10 등) // targetIndex를 구할 때 DivideByZero 예외가 발생하게 되므로 처음과 끝을 확인 else if (arr[start] == arr[end]) { if (arr[start] == target) { return start; } else { throw new Exception("데이터를 찾을 수 없습니다."); } } // double로 계산 후 다시 int로 변환한 이유는 아래와 같다. // 10 ~ 30까지의 범위에서 12값을 찾을 때, 아래 수식은 // ((12 - 10) / (30 - 10) * (20 - 0)) + 0 => (1/10 * 20) + 0 이 된다. // 그러나, 1/10은 int에서는 0으로 변환되므로 최종값은 0이 되기 때문에 double형으로 먼저 계산 후 int형으로 바꿔주는 방식으로 구현해야 한다. targetIndex = (int)((double)(target-arr[start]) / (arr[end] - arr[start]) * (end - start)) + start; System.out.println("targetIndex : " + targetIndex); if (arr[targetIndex] == target) { return targetIndex; } else if (target < arr[targetIndex]) { return search(arr, start, targetIndex - 1, target); } else { return search(arr, targetIndex + 1, end, target); } } }
- 테스트
[InterpolationBasicMain.java]
package Search.Interpolation; import java.util.Arrays; public class InterpolationBasicMain { private static final int MAX_COUNT = 30; public static void main(String[] args) throws Exception { int[] arr = new int[MAX_COUNT]; for(int i=0; i<MAX_COUNT; i++) { // 0 ~ MAX_COUNT 범위내의 난수를 생성 arr[i] = (int)(Math.random() * MAX_COUNT); } Arrays.sort(arr); System.out.println("배열 값 출력"); for(int i : arr) { System.out.print(i + " "); } System.out.println(); int search = InterpolationBasic.search(arr, 0, arr.length - 1, 5); System.out.println("탐색완료 : " + search); } }
- 결과
이진 탐색과 보간 탐색 비교
데이터 100개를 임의생성한 후 특정 값을 찾는데 몇 번의 탐색 인덱스값을 설정했는지 확인
package Search;
import Search.BinarySearch.BinarySearchBasic;
import Search.Interpolation.InterpolationBasic;
import java.util.Arrays;
public class SearchCompareTest {
private static final int MAX_COUNT = 100;
public static void main(String[] args) throws Exception {
int[] arr = new int[MAX_COUNT];
for(int i=0; i<MAX_COUNT; i++) {
// 0 ~ MAX_COUNT 범위내의 난수를 생성
arr[i] = (int)(Math.random() * MAX_COUNT);
}
Arrays.sort(arr);
int searchBinary = BinarySearchBasic.search(arr, 0, arr.length - 1, 10);
System.out.println("searchBinary : " + searchBinary);
System.out.println();
int searchInter = InterpolationBasic.search(arr, 0, arr.length - 1, 10);
System.out.println("searchInter : " + searchInter);
}
}
결과
이진 탐색의 경우 무조건 절반씩 잘라서 탐색하기 때문에 7번의 탐색만에 찾았지만, 보간 탐색의 경우 탐색 대상이 앞쪽에 있는지, 뒷쪽에 있는지 확인한 후 탐색 인덱스를 결정하기 때문에 성능이 더 좋다. (데이터 산포가 적을 경우에만)
'0x20. 알고리즘 > 0x21. 이론' 카테고리의 다른 글
[JAVA] 이진 탐색 트리 (Binary Search Tree, BST) (0) | 2021.11.22 |
---|---|
[JAVA] 기수 정렬 (Radix Sort) (0) | 2021.11.11 |
[JAVA] 퀵정렬 (Quick Sort) (0) | 2021.11.09 |
[JAVA] 힙 정렬 (Heap Sort) (0) | 2021.11.08 |
[JAVA] 합병(병합) 정렬 (Merge Sort) (2) | 2021.11.05 |