새소식

반응형
컴퓨터공학 (Computer Science)/┣ 탐색 | Search

이진탐색의 Variation

  • -
반응형

1. Uniform binary search

이진탐색의 mid는 현재 값에서 항상 일정한 값 만큼만 차이나게 된다. 즉 현재 a[mid]의 값을 검사했다면 다음에 검사할 값은 a[mid + k] or a[mid - k] 이다. 따라서 매 루틴마다 다음 mid까지의 거리 k를 미리 look-up table에 저장해둔다. 이 결과를 통해 mid를 정하는 산술연산이 불필요하게 된다. 같은 배열에 대해 많은 쿼리를 처리해야하는 환경에서 빠르게 동작한다.


2. Exponential binary search

이 방법은 target의 범위를 고정하는 방법이다. 시작부터 bound의 범위를 2배로 늘려가며 target의 상한을 찾는다. 상한을 찾았으면 target의 범위는 [bound / 2, bound+1] 사이에 있으므로 이 범위에서 이진탐색을 수행한다. 탐색 범위의 측면에서는 상한이 배열의 끝까지가도 bound / 2가 배열의 정중앙 뒤쪽에 있다면 결국 [bound / 2, size] 만큼만 탐색을 하기 때문에 bound / 2가 배열의 후반부에 있을 수록 유리하다. 하지만 경험적으로 target이 배열의 초반에 위치하지 않는 이상 큰 이득은 보지 못한다고 한다.


3. Interpolation binary search

이 방법은 mid를 단순하게 high와 low의 중간값이 아니라, target이 실제 high, low의 값에 비해 얼마나 떨어지있는지를 반영하는 방법이다. 배열이 1, 4, 7, 10, ... 이런식으로 uniformly distruted 할 때 쓰인다. 그렇지 않으면 적절한 mid값을 찾지 못한다. 시간복잡도는 $O(log(log\; n))$이다.


4. Implement

다음은 이진탐색의 구현이다. 파이썬으로 구현되어있으며, recursive, non-recursive, alternative, uniform, exponential, interpolation의 6가지 이진탐색 기법을 구현했다.

import random as rand
import time
import math

def Binary_Search_recursive(a, target, low, high):
    
    if low >= high:
        return -1
    
    mid = (low + high) // 2
    if a[mid] > target:
        return Binary_Search_recursive(a, target, low, mid - 1)
    elif a[mid] < target:
        return Binary_Search_recursive(a, target, mid+1, high)
    else:
        return mid
    
def Binary_Search_nonRecursive(a, target, low, high):
    
    while low <= high:
        mid = (low + high) // 2
        if a[mid] > target:
            high = mid - 1
        elif  a[mid] < target:
            low = mid + 1
        else:
            return mid
        
        return -1

def alternative_Binary_Search(a, target, low, high):
    while low < high:
        mid = (low + high) // 2
        if a[mid] > target:
            high = mid - 1
        else:
            low = mid + 1
    if a[low] == target:
        return low
    return -1

def uniform_Binary_Search(a, target):
    mid = lut[0] - 1
    i = 0
    
    while True:
        if target == a[mid]:
            return mid
        elif lut[i] == 0:
            return -1
        elif target < a[mid]:
            mid -= lut[i]
        else:
            mid += lut[i]
        i += 1
    

def build_lookupList(a):
    lookupList = []
    
    N = math.floor(math.log(len(a)))
    power = 1
    p = -1
    while p != 0:
        half = power
        power *= 2
        p = (N+half) // power
        lookupList.append(p)
    
    return lookupList

def Binary_Search_exponential(a, target, low, high):
    bound = 1
    while(bound < len(a) and a[bound] < target):
        bound *= 2
    
    return Binary_Search_nonRecursive(a, target, bound // 2, min(bound + 1, len(a)-1))

def Binary_Search_interpolation(a, target, low, high):
    
    while low <= high:
        mid = mid = low + (((high - low) // (a[high] - a[low])) * (target - a[low]))
        if a[mid] > target:
            high = mid - 1
        elif  a[mid] < target:
            low = mid + 1
        else:
            return mid
        
        return -1

5. Result

다음 이미지는 위 코드를 반복 수행한 결과이다. uniformly distruted한 배열을 쓰지 않으니 interpolation method가 일반 이진탐색과 큰 차이가 없어졌다.

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.