CS/자료구조 - 2

  1. 해싱- 선형/이차/이중 해싱법 구현, 비교 2016.02.17
  2. 해싱(Hashing)에 관한 공부 2016.02.17

해싱- 선형/이차/이중 해싱법 구현, 비교

CS/자료구조 2016. 2. 17. 04:11

해싱에서 충돌을 회피하는 방식에는 크게 선형조사법, 이차조사법, 이중 해싱법이 있다.

공부하면서 이 방법들에 대해서 좀더 이해하고 실제로 결과물이 어떻게 되는지를 보기 위해 직접 구현해봤다.

우선은 구현한 소스코드를 먼저 공개한다.



#include<time.h>
#include <stdio_ext.h>
#include <sys time.h>

#define TABLE_SIZE 9973
#define INPUT_SIZE 8000
#define DOUBLE_HASH_NUM 9013

int crash = 0;

int linear_probing(int* hash, int input){
        int i;
        int j = 0;
        i = input % TABLE_SIZE;

        while (1){
                if (hash[(i + j)%TABLE_SIZE] == 0){
                        return (i + j)%TABLE_SIZE;
                }
                j++;
                crash++;
        }

}


int quadratic_probing(int* hash, int input){
        int i;
        int j = 0;
        i = input % TABLE_SIZE;

        while (1){
                if (hash[(i + j * j)%TABLE_SIZE] == 0){
                        return (i + j * j)%TABLE_SIZE;
                }
                j++;
                crash++;
        }

}


int double_hashing(int* hash, int input){
        int i;
        int j = 0;
        int step;
        step = DOUBLE_HASH_NUM - input % DOUBLE_HASH_NUM; //h`()
        i = input % TABLE_SIZE;

        if (hash[i] == 0){
                return i;
        }

        else{
                while (1){
                        crash++;
                        j++;
                        if (hash[(i + j*step)%TABLE_SIZE] == 0){
                                return (i + j * step)%TABLE_SIZE;
                        }
                }
        }

}


main(){
        int hash[TABLE_SIZE];
        int input[INPUT_SIZE];
        int i;
        double timechk;
        struct timeval start, end;

        while (1){
                timechk = 0;
                char ch;

                printf("linear(l), quadratic(q), double hashing(d):");
                scanf("%c", &ch);


        int i;
        double timechk;
        struct timeval start, end;

        while (1){
                timechk = 0;
                char ch;

                printf("linear(l), quadratic(q), double hashing(d):");
                scanf("%c", &ch);


                srand(time(NULL));
                for (i = 0; i < INPUT_SIZE; i++){ //input을 초기화
                        input[i] = rand() % 100000 + 1; //1~100000까지의 숫자
                }
                for (i = 0; i < TABLE_SIZE; i++){
                        hash[i] = 0;
                }
                crash = 0;

                if (ch == 'l'){ //linear probing
                        printf("linear probing\n");
                        for (i = 0; i < INPUT_SIZE; i++){
                                int index;
                                index = linear_probing(hash, input[i]);
                                hash[index] = input[i];
                        }
                }


                if (ch == 'q'){
                        printf("quadratic probing\n");
                        for (i = 0; i < INPUT_SIZE; i++){
                                int index;
                                index = quadratic_probing(hash, input[i]);
                                hash[index] = input[i];
                        }
                }

                if (ch == 'd'){
                        printf("double hashing\n");
                        for (i = 0; i < INPUT_SIZE; i++){
                                int index;
                                index = double_hashing(hash, input[i]);
                                hash[index] = input[i];
                        }
                }

                gettimeofday(&end, NULL);
                timechk = (double)(end.tv_sec) + (double)(end.tv_usec) / 1000000.0 - (double)(start.tv_sec) - (double)(start.tv_usec) / 1000000.0;
                printf("충돌수: %d\n", crash);
                printf("시간: %f초\n", timechk);
                __fpurge(stdin);


        }

}


C언어는 어떻게 발악을 해도 항상 코드가 더러워진다... 간단한 내용도 필연적으로 길어지는듯. while(1) 안쪽에도 스위치 케이스 문을 썼으면 더 깔끔햇을텐데... 되는대로 하다보니 이미 늦었다. 어쨋든 잘 작동하니까 됐다.

워낙 유명한 알고리즘이라 딱히 주석을 넣을 필요성도 느끼지 못했다.

약간의 설명만 추가하자면 9973이라는 10000에 아주 가까운 소수개의 테이블을 가진 해시에 mod 연산 결과에 따라서 데이터를 집어넣는 시뮬레이션이다. 데이터는 랜덤값으로 만들어줬고, 이중해싱은 테이블 사이즈보다 약간 작은 소수인 9013을 사용했다.



우선 전체 테이블 사이즈의 약 80%정도인 8000개의 인풋을 넣는 시뮬레이션이다. 코드 윗쪽의 INPUT_SIZE를 8000으로 맞춰줬다.

결과는 역시 선형조사법이 가장 안좋다. 충돌수도 압도적으로 높고 시간도 가장 길었다.

특이한 점이 이중 해싱 방식이 충돌은 더 많이 일어났는데도 총 시간은 더 적게 걸렸다. 원래 그런거 같긴 한데 어쩌면 내가 구현하는 방식이 잘못되서 그런걸지도.




다음으로 테이블 사이즈의 60%정도가 찬다고 가정하고 INPUT_SIZE를 6000개정도로 줘봤다.


크게 다르지는 않은데 특이하게 이중해싱법의 충돌수가 선형조사법을 넘어섰다. 그럼에도 여전히 총 시간은 가장 적다.




다음으로 약 40%인 4000을 INPUT_SIZE로 주었다.


위 결과와 거의 비슷했다. 특이한 점은 없음.




이렇게 한번 충돌방지법들을 실제로 구현해보고 성능을 비교해보았다. 다음에는 체이닝 기법도 한번 구현해 봐야겠다.



'CS > 자료구조' 카테고리의 다른 글

해싱(Hashing)에 관한 공부  (0) 2016.02.17
Tags
Social

해싱(Hashing)에 관한 공부

CS/자료구조 2016. 2. 17. 03:42

자료구조에 대해서 공부를 시작하려는데 책하나를 가지고 앞에서부터 주욱 공부해 나가는게 좋은 방식이겠지만...

어떤 계기가 있어서 해싱에 대해서 좀 찾아보게 됐고 공부야 뭐 여러가지 그림이나 이해를 돕기위한 자료들로 했지만 일단은 내 머리속에서의 흐름대로 정리하기 위해 간단하게 글로 포스팅한다.


일반적으로 탐색 알고리즘은 키값을 비교해서 그 데이터의 주소를 얻는다.

그래서 속도를 빠르게 하려고 비교 횟수를 줄이기 위한 노력들을 많이 하는데... 주로 데이터를 미리 정렬해놓고 이진검색을 하거나 보간검색을 하는 등 확률적으로 최대한 키값을 빨리 찾아내도록 알고리즘을 만든다.


하지만 해싱을 이런 방식과는 완전히 다르게 탐색시간을 획기적으로 줄일 수 있는 듯 하다.

해싱은 해시테이블에 데이터를 집어넣을 때 일정한 규칙을 가지고 키값을 연산하여 해당 주소에 집어넣는다.

그러므로 해당 키값에 대한 데이터를 찾을때는 키값을 연산하여 주소를 구할수 있고 옵티멀한 경우 한번의 연산만으로도 데이터의 주소를 얻을 수 있다.

이때 연산하는 규칙으로 주로 mod연산을 많이 사용한다고 한다.


하지만 연산에 의한 값이므로 다른 키값에 대한 연산결과가 테이블의 같은 주소를 가르킬 수도 있다.

이런 경우를 충돌이라고 하는데 이 충돌을 해결하기 위해서 충돌 발생시 비어있는 다른 주소를 찾아주는 선형조사법, 이차조사법, 이중 해싱법 등을 사용한다.

선형조사법은 충돌 발생시 바로 다음 주소에 데이터를 집어넣는 방식이다. 단순한 방식이지만 데이터가 특정 구간에 몰리는 clustering 현상이 발생하기 쉽다.

상식적으로 생각해봐도 데이터를 그 바로 다음 부분에 집어넣으면 해시테이블에서 데이터가 들어있는 부분들이 붙어있게 되고 그 붙어있는 덩어리가 커질수록 새로운 키 연산결과가 그 큰 덩어리 가운데 어딘가일 확률도 높아진다. 그냥 덩어리만 붙어있으면 별 상관이 없겠지만 덩어리 바로 다음부분의 빈공간을 찾을때까지 계속 1씩 증가하며 확인과정을 거쳐야 하므로 연산 효율이 크게 떨어진다.

이차조사법은 선형조사법의 군집화 문제를 피하기 위해서 1씩 증가된 주소에 넣는것이 아니라 1 4 9 16 이런식으로 제곱단위로 증가된 주소에 데이터를 넣는 방식이다. 완전히 극복되지는 않는듯.

이중 해싱법은 새로운 연산, 보통 C - key mod C(C는 테이블 크기보다 약간 작은 소수) 이런식으로 많이 정해서 쓰는데 이 연산의 값만큼 뒷쪽 주소에서 빈공간을 찾는 방식이다. 마찬가지로 완전히 극복되지는 않는 듯.


또 아예 충돌이 발생하면 뒷쪽에 연결리스트로 달아버리는 체이닝 기법도 있다. 보통 모든경우에 결과가 위 방식들보다 빠르다고 알고있다.


'CS > 자료구조' 카테고리의 다른 글

해싱- 선형/이차/이중 해싱법 구현, 비교  (0) 2016.02.17
Tags
Social
< 1 >