본문 바로가기

프로그래밍/알고리즘

해싱 (Hashing) (3)

Hashing


좋은 해쉬 함수의 조건

- 현실에서의 키들은 무작위값이 아니다.

- 만일 키들의 통계적인 분포에 대해 알고 있다면 이를 활용하여 해쉬 함수를 고안할 수 있겠지만 현실적으로 할 수 없는 방법이다.

- 키들이 어떤 특정한(가시적인) 패턴을 가지더라도 해쉬함수 값이 불규칙적이도록 하는 것이 바람직하다. 이는 곧 해쉬합수의 값이 키의 특정 부분에 의해서만 결정되지 않도록 만들어야 한다는 뜻이다.


해쉬 함수를 만드는 방법

- Division 기법

h(k) = k mod m => 거의 모든 해쉬함수들이 가장 마지막에 수행하는 경향이 있다.

Ex) m = 20, k = 91 -> h(k) = 11

장점 : 한 번의 mod연산으로 계산하기 때문에 속도가 빠르다.

단점 : 어떠한 m값에 대해서는 해쉬 함수의 값이 키 값의 특정 부분에 의존하여 결정되는 경우가 있다. 예를 들어 $m=2^p$라면 어떠한 키의 하위 p비트가 해쉬 함수의 값이 된다.

- Multiplication 기법

0에서 1사이의 상수 A를 선택한다 (0 < A < 1)

kA의 소수부분만을 취한다.

소수 부분에 m을 곱한 다음 소수점 아래를 버린다.

Ex)  m = 8, word size = w = 5, k = 21

A = 13 / 32를 선택했다.

kA = 21 * 13 / 32 = 273 / 32 = 8 + 17 / 32 (정수 부분인 8을 제거한다.)

m (kA mod 1) = 8 * 17 / 32 = 17 / 4 = 4.~

h(21) = 4


Hash code in Java

- Java의 Object클래스는 hashCode()메소드를 가지고 있다. 따라서 모든 클래스들은 hashCode()메소드를 상속받게 된다. 이 메소드는 하나의 32비트 정수를 반환하게 된다. (음수값일 수도 있다.)

- 만약 x.equals(y)라면 x.hashCode() == y.hashCode()이다. 하지만 그의 역은 성립하지 않는다.

- Object 클래스의 hashCode()메소드는 객체의 메모리 주소를 반환한다고 알려져 있다. (but, it's implementation-dependent.)

- 필요에 따라 각 클래스마다 이 메소드를 override하여 사용한다.

Ex) Integer 클래스는 정수값을 hashCode로 사용한다.


해쉬함수의 예 : hashCode() for Strings in Java

- h(s) = $31^{L-1}$ * $s_0$ + ... + $31^1$ * $s_{L-2}$ + $31^0$ * $s_{L-1}$

- L : String의 길이, s : 유니코드값

pulbic final class String {

private final char[] s;

...

public int hashCode() {

int hash = 0;

for (int i = 0; i < length(); i++)

hash = s[i] + (31 * hash);

return hash;

}

}


사용자 정의 클래스의 예시

- 데이터 클래스 내에 있는 모든 멤버들을 사용하여 hashCode를 생성한다.

- 만약 데이터 클래스 내에서 한 가지 멤버만 사용해 hashCode를 생성한다면 그 멤버와 값이 같기만 하다면 해쉬코드의 값이 같아지는 문제가 발생하게 된다.

public class Record {

private String name;

private int id;

private double value;

...

public int hashCode() {

int hash = 17; // 0이 아닌 상수값은 아무거나 상관없음 (보통은 소수를 사용한다.)

hash = 31 * hash + name.hashCode();

hash = 31 * hash + Integer.valueOf(id).hashCode();

hash = 31 * hash + Double.valueOf(value).hashCode();

return hash; 

}

}


hashCode와 hash function

- Hash Code : $-2^31$에서 $2^31$사이의 정수범위를 가지고 있다.

- Hash Function : 0에서 M - 1까지의 정수 범위를 가지고 있다. (배열 인덱스)

- Ex

private int hash(Key key) {

return (key.hashCode() & 0x7fffffff) % M;

}


HashMap in Java

- TreeMap 클래스와 유사한 인터페이스를 제공해준다. (둘 다 java.util.Map인터페이스를 구현한 클래스이다.)

- 내부적으로는 하나의 배열을 해쉬 테이블로 사용한다.

- 해쉬 함수는 일반적인 구성을 취하고 있다. (바로 위의 예시와 유사하다.)

- chaining으로 충돌 문제를 해결하고 있다.

- load factor를 지정할 수 있다. (0 ~ 1 사이의 실수값)

- 저장된 키의 개수가 load factor를 초과하게 된다면 더 큰 배열을 할당하고 저장된 키들은 재배치시키게 된다. (re-hashing)

- 코드

HashSet<MyKey> set = new HashSet<>();

set.add(MyKey); // 요소 추가 가능

if (set.contains(theKey)) // 요소 존재 여부 검사 가능

...

int k = set.size();

set.remove(theKey); // 요소 제거 가능

Iterator<MyKey> it = set.iterator(); // iterator 가능

while (it.hasNext()) {

MyKey key = it.next();

if (key.equals(aKey))

it.remove();

}

'프로그래밍 > 알고리즘' 카테고리의 다른 글

그래프에서의 BFS  (0) 2019.06.10
그래프의 개념과 표현  (0) 2019.06.10
해싱 (Hashing) (2)  (0) 2019.06.09
해싱 (Hashing) (1)  (0) 2019.06.08
Red Black Tree (2)  (0) 2019.06.05