Ch.08-4 Hashing (해싱) [CPA340] Algorithms and Practice Youn-Hee Han http://link.kut.ac.kr Hashing 기본 개념 해싱 (Hashing) 하나의 문자열을 더 짧은 길이의 값으로 변환하는 것 사전적 의미: 잘게 썰다. 예제를 통한 해싱에 대한 이해 (1/2) 0에서 99사이의 정수로 된 키(key)가 있고, 100개의 저장해야 할 정보 가 있다고 하자. 그러면 크기가 100인 배열 S를 만들어서 저장하면 빠 른 시간 안에 검색할 수 있다. i s[i] 그런데 만약 키(key)가 13자리 주민등록번호라면 키 값 저장을 위하 여 너무 많은 저장장소가 필요하게 된다. Page 2 Hashing 기본 개념 예제를 통한 해싱에 대한 이해 (2/2) 해법: 0 ~ 99의 인덱스를 가진 크기가 100인 배열을 만든 후에, 주민등록번호 키 값을 0~99 사이의 값을 가지도록 해쉬(hash)한다. 여기서 해쉬함수는 13자리 실제 키 값을 배열의 첨자 값 (0~99)으로 변환하는 함수이다. 해쉬함수의 예: h(key) = key % 100 13자리 키 값이 2자리 키 값으로 변환됨 주민증록번호 (13자리) 해시값 (2자리) i s[i] 해싱의 장점 해시 연산은 매우 짧은 시간안에 이루어진다. 짧은 해시 키(해싱된 결과 인덱스)를 사용하여 항목을 찾으면 원래의 값을 이용하여 찾는 것보다 더 빠르며 메모리도 덜 차지한다. 읽을 거리: http://k.daum.net/qna/view.html?qid=00WN2 Page 3 Java HashMap 자료구조 Java HashMap 자료구조 import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Number { public static void main(String[] args) throws Exception { Map mMap = new HashMap(); mMap.put("PostgreSQL", "Free Open Source Enterprise Database"); mMap.put("DB2", "Enterprise Database , It's expensive"); mMap.put("Oracle", "Enterprise Database , It's expensive"); mMap.put("MySQL", "Free Open SourceDatabase"); Iterator iter = mMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry mEntry = (Map.Entry) iter.next(); System.out.println(mEntry.getKey() + " : " + mEntry.getValue()); } mMap.put("Oracle", "Enterprise Database , It's free now ! (hope)"); System.out.println("One day Oracle.. : " + mMap.get("Oracle")); } } 4 Web Programming Hashing – 충돌(Collision) 해싱의 문제점: 충돌(Collision) 2개 이상의 키가 같은 해쉬값을 갖는 경우 충돌(collision)이 발생 [충돌이 일어나지 않을 확률] 100개의 키가 있고, 각 키가 100개의 인덱스로 해시될 것이다. 한편, 임의의 인덱스로 해쉬될 확률이 같다고 하자. 이 때 모든 키가 모두 다른 인덱스로 해쉬될 확률은 다음과 같다. 100 p100 = 100 π100 [의미] 매우 작은 확률! 즉, 지속적인 해싱에 의해 최소한 2개의 키가 하나의 인덱스로 해시, 즉 충돌이 일어날 가능성이 꽤 높다. 이러한 해시 충돌 (Hash Collision)을 어떻게 해결할 것인가? Page 5 Hashing – 충돌(Collision) 충돌 해결법: 오픈 해싱(open hashing) 같은 해쉬값을 갖는 키들을 동일한 Bucket(바구니)에 모아 놓는다. 주로 Bucket는 연결 리스트(linked list)로 구현 한다. Bucket 내부를 검색할 때는 순차검색(또는 효율 높은 다른 검색)으로 한다. h(key) = key % 100 구현 방법 각 Bucket을 가리키는 포인터 배열 Bucket[]을 만들고 각 배열의 포인터 값은 해당 Bucket의 연결 리스트를 가리키도록 한다. 값 i 로 해시되는 키 값들은 모두 Bucket[i]가 가리키는 연결 리스트에 순 차적으로 위치시킨다. Page 6 Hashing – 충돌(Collision) 충돌 해결법: 오픈 해싱(open hashing) Bucket의 수가 키의 개수와 같을 필요는 없다. 하지만… 키의 수보다 Bucket의 수가 적으면 충돌은 필연적이다. 만약 Bucket의 수가 1개라면 … 레코드의 검색은 단순한 키 값의 순차 검색으로 퇴보 일반적으로 레코드의 크기에 비해 포인터 변수는 상대적으로 작으므 로 최소한 레코드의 수만큼의 Bucket을 사용하는 것이 합당하다. 만약 Bucket의 수가 100개일 때 100개의 키가 같은 Bucket으로 들어갈 확률은? 1 100 100 × ( ) = 10-198 100 [의미] 매우 작은 확률이다. Page 7 Hashing – 충돌(Collision) 오픈 해싱(open hashing) 방법의 분석 Assumption: 만약 n개의 키와 m개의 Bucket이 있고 각 키 값들이 Bucket 마다 균일하게 분포 및 저장 되어있다. [기본적 사실] 각 Bucket 마다 n/m 개의 키가 존재한다. n [정리 8.4] 실패하는 검색의 경우 총 비교횟수는 다음과 같다. m [정리 8.5] 각 키마다 검색되는 확률이 동일하다면 성공한 검색의 평균 비교횟수는 다음과 같다. n/m 1 m n / m(n / m + 1) n 1 = + ∑k • n / m = n • 2 2m 2 k =1 교재 P21의 알고리즘 1.1 분석과 동일한 방법으로 분석: 임의의 Bucket에 n 서의 평균 검색시간은 개의 키를 순차검색하는 평균시간과 같다 m Page 8 Hashing – 충돌(Collision) 오픈 해싱(open hashing) 방법의 분석 그렇다면, n개의 키가 주어져 있을 때 검색을 빠르게 하기 위하여 이 분 검색을 사용해야 할까? 아니면 해시 방법을 통해 저장한 후 검색을 해야 할까? Example] - 256개의 키 이분 검색 log2256 = 8 번의 평균 비교검색 오픈 해싱 n 1 256 1 Bucket이 16개일 때: + = + = 8.5 번의 평균 비교검색 2m 2 2 • 16 2 n 1 256 1 Bucket이 128개일 때: + = + = 1.5번의 평균 비교검색 2m 2 2 • 128 2 n 1 256 1 Bucket이 256개일 때: + = + = 1.0 번의 평균 비교검색 2m 2 2 • 256 2 Page 9 Hashing – 충돌(Collision) 오픈 해싱(open hashing) 방법의 분석 [정리 8.6] 만약 n개의 키와 m개의 Bucket이 있고 각각의 키가 각 Bucket 으 로 해시될 확률이 동일하다면, 한 개의 Bucket에 최소한 k개의 키가 들어 있 을 확률은 다음보다 작거나 같다. (즉, 확률의 상한-Upperbound) 한번의 비행기 여행에서 사고가 날 확률:6 *10- 6 -6 자동차 사고로 죽을 확률: 270*10 [결론] Bucket의 수가 충분하고 해시 함수가 좋을 때 Collision 발생에 의해 임의의 Bucket내의 레코드 수가 이분 검색의 검색 횟수인 log2n보다 많아질 확률은 극히 적다. Page 10 http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html#HashMap(int) Java HashMap 자료구조 Capacity the number of buckets in the hash table Initial capacity the capacity at the time the hash table is created. load factor a measure of how full the hash table is allowed to get before its capacity is increased. When “# entries in the hash table” > “load factor * current capacity”, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets. Higher load factor values decrease the space overhead but increase the lookup cost 1) The expected number of entries in the map, 2) its load factor, 3) initial capacity should be considered so as to minimize the number of rehash operations. If the initial capacity > the maximum number of entries * 1 / load factor, no rehash operations will ever occur.