Hash
Table of contents
해시 (Hash)
용어
-
해싱(Hashing)
- Key 값에 산술연산을 적용하여 특정 항목의 테이블 주소(해시값)를 구하는 과정
-
해시 함수(Hash Function)
- 해싱을 할 때 사용하는 산술 연산 함수(알고리즘)
- 고정된 길이의 데이터로 매핑하는 함수
- 중복없이 해시값을 고르게 만들어내는 함수가 좋은 함수 - 충돌이 일어나지 않을수록 좋음
-
해시 충돌(Hash Collision)
- 다른 내용의 데이터가 같은 해시값을 가질 때 일어남
- 해싱 과정에서 Input이 무한할 때 Output이 유한하기에 해시 충돌은 반드시 발생 (비둘기집 원리)
해시 테이블
정의
- 연관 배열에서의 Key값으로 고유한 index를 생성하여 버킷 또는 슬롯이라는 저장소(배열)에 (Key, Value)로 데이터를 저장하는 자료구조
- 파이썬 dictionary, 자바 Map이 해시테이블의 대표적인 예
특징
- 기본 연산으로 search, insert, delete
- 순차적으로 데이터 저장 X
- 수정 가능하다 (=mutable)
장점
- 많은 데이터를 유한한 개수의 해시값으로 매핑하면서 작은 크기의 캐시메모리로 프로세스 관리 가능
- 해당 색인(해시값)만 알면 해시테이블의 데이터에 접근 가능
단점
- 주소가 동일할 경우 충돌(Collision)을 해결하기 위한 별도의 자료구조 필요
시간복잡도
- 평균: O(1)
- 최악: O(n) = 연결 리스트 탐색 시간 복잡도
해시 함수
나눗셈법(Division Method)
정의
- 해시 함수를 적용하고자 하는 값을 테이블 크기(N)으로 나눈 나머지 해시값으로 사용하는 방법
- h(k) = k mod N
- 이때 N은 소수를 사용하는 것이 좋음
- 가장 기본적이고 많이 쓰이는 방법
코드
def hash_func(self,key):
return key % self.size
</br>
폴딩방법(Folding Method)
정의
- 레코드 키값을 여러부분으로 나눈 후 각 부분의 값을 더하거나 XOR(배타적 논리합)한 값을 해시값으로 사용하는 방법
종류
- 이동 폴딩(Shift Folding)
- 해시 테이블 크기의 자릿수로 키 분해
- 분해된 키들을 더한다
- 더한 값이 테이블 크기를 초과하면 초과한 자릿수를 버린다
- 경계 폴딩(Boundary Folding)
- 해시 테이블 크기의 자릿수로 키 분해
- 나누어진 각 부분의 경계 부분 값을 역으로 정렬
- 분해된 키들을 더한다
- 더한 값이 테이블 크기를 초과하면 초과한 자릿수를 버린다
</br>
중간제곱 방법(Mid Square Method)
정의
-
중간 제곱 방법은 키값을 제곱한 결과값에서 중간에 있는 적당한 비트를 주소로 사용하는 방법
-
제곱한 값의 중간 비트들은 대개 키의 모든 값과 관련이 있음
-
서로 다른 키값은 서로 다른 중간 제곱 함수값을 가짐
</br>
자릿수 분석 방법(계수 분석법, 숫자 분석법)
정의
- 키 값을 이루는 숫자의 분포를 분석하여 비교적 고른 자리를 필요한 만큼 택해서 홈 주소로 삼는 방법
예제
- 테이블 크기: 1000
- 홈 주소: 0 ~ 999(3자리)
키 레코드 | 홈 주소 |
---|---|
012-452-0236 | 426 |
012-153-0530 | 150 |
015-342-0935 | 395 |
012-752-1032 | 702 |
012-852-0470 | 840 |
- 결과: 4, 8, 10열을 홈 주소로 선택
- 왼쪽 3자리 숫자는 거의 동일하므로 제거
- 5, 6, 7, 9열 역시 비슷한 숫자가 많으므로 무시
- 비교적 고른 분포인 4, 8, 10열을 선택하여 홈 주소로 사용
</br>
기수 변환 방법
정의
-
키 값의 진수를 다른 진수로 간주하여 주소 크기를 초과한 높은 자릿수를 절단하고, 이를 다시 주소 범위에 맞게 조정하여 해시값으로 사용하는 방법
- 해시 테이블 크기 = 1000
- 키 값 B2538(10)을 16진수로 간주해 10진수로 다시 계산
- (1 * 16^5) + (3 * 16^4) + (2 * 16^3) + (5 * 16^2) + (4 * 16^1) + (8 * 16^0) = 1254728(10)
- 해시 테이블의 크기가 1000이므로 해시값은 728이다
해시 충돌
개방 주소법(Open Addressing)
정의
- 해시 충돌이 발생하면 테이블 내의 새로운 주소를 탐사(Probe)한 후, 비어있는 곳에 충돌된 데이터를 입력하는 방법
특징
- 삭제의 경우 충돌에 의해 저장된 데이터가 검색되지 않을 수 있다.
- 삭제한 위치에 더미노드를 삽입(다음 위치까지 연결 해 주는 역할)한다.
-
더미 노드의 개수가 일정 이상이 되었을 경우, 주기적으로 새로운 배열을 만들어 다시 해시 해줘야 성능이 유지 가능하다.
- 종류로 선형 조사법, 이차 조사법 등 이 존재한다.
</br>
선형 조사법(linear probing)
정의
- 해시테이블을 선형, 순차적으로 탐색하여 비어있는 공간이 나올 때까지 탐사하는 방법이다.
원리
-
ex) h(k) = k % 7
- 해쉬 키값이 [2,9,16,23]이 나오면 h(k)는 모두 2이다.
- 배열 T
- 2 % 7 = 2 => T[2]
- 9 % 7 = 2 => T[2+1] 탐색 후 비어있으면 => T[3]
- 16 % 7 = 2 => T[2+1+1] 탐색 후 비어있으면 => T[4] - 비어있지 않으므로 T[2+1+1+1] 탐색 후 비어있으면 => T[5]
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
☑ | ☑ | E85H | ☑ |
특징
- 만약, 조사를 시작했던 위치로 다시 돌아오게 되면 테이블이 가득 찬 것이다.
장점
- 구조가 간단하고 캐시의 효울이 높다.
단점
- 최악의 경우, 해시테이블 전체를 검색해야 하는 상황이 발생한다.
- 데이터의 클러스터링에 가장 취약
- 군집화(clustering) 문제가 발생할 수 있다.
</br>
2차탐사 방법(Quadratic Probing)
정의
- 원래 저장할 위치로부터 조금 더 멀리 떨어진 영역을 찾는 방법으로 해쉬 키 값에 충돌 횟수의 제곱한 수를 더한 값에 저장한다.
원리
-
ex) h(k) = k % 7
- 해쉬 키값이 [2,9,16]이 나오면 h(k)는 모두 2이다.
- 배열 T
- 2 % 7 = 2 => T[2]
- 9 % 7 = 2 => T[2+1^2] = T[3]
- 16 % 7 = 2 => T[2+2^2] = T[6]
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
☑ | ☑ | ☑ |
</br>
체이닝 방법(Chaining)
정의
- 동일한 해시 값(Hash Value)을 갖는 Key들을 다른 버킷에 넣는 것이 아닌 동일한 버킷에 연결리스트 형식으로 연결하는 방법이다.
단점
- 하나의 해시 값에 여러 엔트리가 몰릴 수 있다. 이렇게 되면 배열, 리스트와 같은 선형적인 구조를 갖는 것과 마찬가지이므로 탐색 시 최악의 경우 O(N)의 시간복잡도를 갖게되어 탐색이 빠르다는 해시 테이블의 장점이 퇴색된다.
해결법
- 버킷의 크기 동적으로 늘리기
- 비선형적 구조로 entry 저장하기
- 트리 -> log(n)