๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿค– data structures & algorithms

Data Structures & Algorithms with Javascript : Hashing

[์ด์ „ ๊ธ€] 2024.07.22 - [๐Ÿค– data structures & algorithms] - Data Structures & Algorithms with Javascript : Dictionaries

Hashing์€ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€์™€ ๋ฐ˜ํ™˜์„ ๋น ๋ฅด๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•œ ๋ฐ์ดํ„ฐ ์ €์žฅ ๊ธฐ์ˆ ์ด๋‹ค.
Hash table์ด๋ผ๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, hash table์€ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€, ์‚ญ์ œ, ๋ฐ˜ํ™˜์€ ๋ฐฐ๋ฌด ๋น ๋ฅด์ง€๋งŒ 
์ตœ์†Œ๋‚˜ ์ตœ๋Œ€ ๊ฐ’์„ ๊ตฌํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ฒ€์ƒ‰์€ ๋น„ํšจ์œจ์ ์ด๋‹ค.

๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป ์˜ค๋Š˜์€ Hash table์ด ๋ฌด์—‡์ธ์ง€, ํด๋ž˜์Šค ๊ตฌํ˜„๋ฒ•, collision๊ณผ ๊ทธ ํ•ด๊ฒฐ๋ฒ•์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์ž.

Hashing ์ด๋ž€

Hash table ์ž๋ฃŒ ๊ตฌ์กฐ๋Š” array๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด์ง„๋‹ค.

์ด array๋Š” 0๋ถ€ํ„ฐ ์ •ํ•ด์ง„ ํฌ๊ธฐ์˜ ์—˜๋ฆฌ๋จผํŠธ๋กœ ๊ตฌ์„ฑ๋œ๋‹ค. (ํ•„์š”์— ๋”ฐ๋ผ ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.)

๊ฐ ๋ฐ์ดํ„ฐ ์—˜๋ฆฌ๋จผํŠธ๋Š” dictionary ์ž๋ฃŒ ๊ตฌ์กฐ์—์„œ ์‚ดํŽด๋ดค๋˜ ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๊ฒŒ ํ‚ค(key)๋กœ ์ €์žฅ์ด ๋œ๋‹ค.

๋ฐ์ดํ„ฐ๋ฅผ hash table์— ์ €์žฅํ•  ๋•Œ, 0๋ถ€ํ„ฐ hash table์˜ ํฌ๊ธฐ์— ๋Œ€์‘ํ•˜๋Š” ํ‚ค ๊ฐ’์„ hash function์œผ๋กœ ๊ตฌํ•ด์„œ ์ €์žฅํ•œ๋‹ค.

hash table์˜ ํฌ๊ธฐ๋Š” ์–ผ๋งˆ๋‚˜ ์ปค์•ผ ํ• ๊นŒ? hash table์˜  array ํฌ๊ธฐ๋Š” ๋ฐ˜๋“œ์‹œ prime number (์†Œ์ˆ˜)๋กœ ์ •ํ•œ๋‹ค.

 

 

์ด๋ฆ„๊ณผ ์ „ํ™”๋ฒˆํ˜ธ๋ฅผ ํ† ๋Œ€๋กœ ํ•œ hashing

 

์ด์ƒ์ ์œผ๋กœ๋Š” hash function์„ ํ†ตํ•ด ๊ฐ ํ‚ค์— ํ•˜๋‚˜์˜ array ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ๋Œ€์‘๋˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

ํ•˜์ง€๋งŒ ํ˜„์‹ค์ ์œผ๋กœ๋Š” ํ‚ค ๊ฐ’์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋Š” ์ˆซ์ž๋Š” ์œ ํ•œํ•œ ๋ฐ˜๋ฉด์—, ํ‚ค์˜ ๊ฐœ์ˆ˜๋Š” ๋ฌดํ•œํ•˜๋ฏ€๋กœ ๊ฐ€๋Šฅํ•œ ํ•œ ํ‚ค๋ฅผ array์— ๊ณ ๋ฅด๊ฒŒ ๋ถ„ํฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ณ ๋ฅด๊ฒŒ ๋ถ„ํฌ๋ฅผ ํ•˜๋ ค๊ณ  ํ•ด๋„ ๋‘ ํ‚ค๊ฐ€ ๊ฐ™์€ hash (hash function์˜ ๊ฒฐ๊ณผ๊ฐ’)์„ ๊ฐ€์งˆ ์ˆ˜๋„ ์žˆ๋‹ค.

์ด ๊ฒฝ์šฐ "collision(์ถฉ๋Œ)"์ด๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•  ์ง€์— ๋Œ€ํ•ด ์ดํ›„์— ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ž.

๐Ÿ—‚๏ธ Hash Table ํด๋ž˜์Šค

hash table ํด๋ž˜์Šค์—๋Š” hash ๊ฐ’์„ ๊ณ„์‚ฐํ•  ๋ฉ”์†Œ๋“œ, ๊ฐ’์„ ์ถ”๊ฐ€ํ•  ๋ฉ”์†Œ๋“œ, hash table๋กœ๋ถ€ํ„ฐ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์†Œ๋“œ, ๊ทธ๋ฆฌ๊ณ  hash table์˜ ๋ฐ์ดํ„ฐ ๋ถ„ํฌ๋ฅผ ๋ณด์—ฌ์ค„ ๋ฉ”์†Œ๋“œ๋ฅผ ํ•„์ˆ˜๋กœ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋‹ค.

Hash Function ์ •ํ•˜๊ธฐ

hash function์„ ๊ณ ๋ฅด๋Š” ๊ธฐ์ค€์€ ํ‚ค์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค.

 

modular hashing

 

ํ‚ค๊ฐ€ ์ •์ˆ˜์ผ ๊ฒฝ์šฐ, ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ hash function์€ array ํฌ๊ธฐ์˜ ํ‚ค modulo ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค๋งŒ ๋ชจ๋“  ํ‚ค๊ฐ€ 0์œผ๋กœ ๋๋‚˜๊ณ  array ํฌ๊ธฐ๊ฐ€ 10์ธ ๊ฒฝ์šฐ์™€ ๊ฐ™์€ ์ƒํ™ฉ์—๋Š” ๋ถ€์ ์ ˆํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— 

array ํฌ๊ธฐ๋ฅผ ์ •ํ•  ๋•Œ ๋ฌด์กฐ๊ฑด ์†Œ์ˆ˜์—ฌ์•ผ ํ•œ๋‹ค. ๋žœ๋ค์˜ ์ •์ˆ˜๊ฐ’์ด ํ‚ค์ผ ๊ฒฝ์šฐ ๋” ๋„“๊ฒŒ ํ‚ค๋ฅผ ๋ถ„ํฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ํ‚ค๊ฐ’์ด string ํƒ€์ž…์ด๋‹ค. string ํƒ€์ž…์— ๋งž๋Š” hash function์„ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” ์ข€ ๋” ์‹ ์ค‘ํ•ด์•ผ ํ•œ๋‹ค.

๊ฐ„๋‹จํ•œ hash function์œผ๋กœ ํ‚ค์˜ ๊ฐ ๋ฌธ์ž์˜ ASCII ๊ฐ’์„ ๋”ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

โ€ผ ์˜ˆ์ œ๋Š” ์ฑ…์˜ ๊ตฌํ˜„์„ ์ฐธ๊ณ ํ•˜์—ฌ ํด๋ž˜์Šค๋กœ ์žฌ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค!!

class HashTable{
	constructor(){
    	this.table = new Array(137);
        
    }
    
    // console.log ํ—ฌํผ ๋ฉ”์„œ๋“œ
    print(item){
    	console.log(item);
    }
    
    // key๊ฐ€ string ํƒ€์ž…์ผ ๊ฒฝ์šฐ 
    simpleHash(data){
    	let total = 0;
        for(let i=0; i< data.length; ++i){
        	total += data.charCodeAt(i);
        }
		this.print("Hash value: " + data + " -> " + total);
        return total % this.table.length;
    }
    
    // hash table ๊ฐ’ ๋ณด์—ฌ์ฃผ๊ธฐ
    showDistribution(){
    	let n = 0;
        for(let i = 0; i < this.table.length; ++i){
        	if(this.table[i] != undefined){
            	this.print(`${i} : ${this.table[i]}`);
            }
        }
    }

    // hash table์— ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
    put(data){
    	let position = this.simpleHash(data);
        this.table[position] = data;
    }
}

const names = ["David", "Jennifer", "Donnie", "Raymond",
"Cynthia", "Mike", "Clayton", "Danny", "Jonathan"];;
const hashTable = new HashTable();
for(let i=0; i < names.length; ++i){
    hashTable.put(names[i]);
}
hashTable.showDistribution();

/**
Hash value: David -> 488
Hash value: Jennifer -> 817
Hash value: Donnie -> 605
Hash value: Raymond -> 730  // ์ถฉ๋Œ!!
Hash value: Cynthia -> 720
Hash value:: Mike -> 390
Hash value: Clayton -> 730 // ์ถฉ๋Œ!!
Hash value: Danny -> 506
Hash value: Jonathan -> 819


35 : Cynthia
45 : Clayton
57 : Donnie
77 : David
95 : Danny
116 : Mike
132 : Jennifer
134 : Jonathan
*/

 

ํ•˜์ง€๋งŒ ์œ„์˜ hash function์˜ ๊ฒฝ์šฐ ํ‚ค ๊ฐ’๋“ค์ด ๊ณ ๋ฅด๊ฒŒ ๋ถ„๋ฐฐ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค.

๊ทธ๋ฆฌ๊ณ  names array์— ์žˆ๋˜ ์ด๋ฆ„๋“ค์ด ๋ชจ๋‘ ํ• ๋‹น๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ๋„ ๋ฌธ์ œ๋‹ค.

Raymond์™€ Clayton์˜ ๊ฐ ๋ฌธ์ž๋ฅผ ASCII ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ˆซ์ž๋ฅผ ๋”ํ•œ ๊ฐ’์ด ๊ฐ™์•„์„œ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๋‹ค.

 

Horner's method

 

์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด hashFunction์„ ๊ฐœ์„ ํ•ด๋ณด์ž.

๋จผ์ € hash table array์˜ ํฌ๊ธฐ๋Š” 100๋ณด๋‹ค ํฐ ์†Œ์ˆ˜์—ฌ์•ผ ํ•œ๋‹ค. 100 ์ด์ƒ์˜ ์†Œ์ˆ˜ ์ค‘์—์„œ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ์ œ์ผ ์ž‘์€ ์ˆซ์ž๋Š” 137์ด๋ผ๋Š” ๊ฑธ ์—ฌ๋Ÿฌ ์‹คํ—˜์„ ํ†ตํ•ด ๋ฐœ๊ฒฌํ–ˆ๋‹ค. Horner’s method์ด๋ผ๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ™œ์šฉํ•˜๋ฉด hash ๊ฐ’ ๊ณ„์‚ฐ์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

ASCII ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜ํ•œ ๊ฐ’๋“ค์„ ๋”ํ•˜๋Š” ๊ณผ์ •์— ์ƒ์ˆ˜์ธ ์†Œ์ˆ˜๋ฅผ ๊ณฑํ•ด์„œ ๋”ํ•ด์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

class HashTable {
    constructor() {
        this.table = new Array(137);
    }
    
    // console.log ํ—ฌํผ ๋ฉ”์„œ๋“œ
    print(item) {
        console.log(item);
    }
    
    // key๊ฐ€ string ํƒ€์ž…์ผ ๊ฒฝ์šฐ 
    simpleHash(data) {
        let total = 0;
        for (let i = 0; i < data.length; ++i) {
            total += data.charCodeAt(i);
        }
        this.print("Hash value: " + data + " -> " + total);
        return total % this.table.length;
    }

    // Horner's method์„ ํ™œ์šฉํ•ด ๊ฐœ์„ ํ•œ hashing function
    betterHash(string) {
        const H = 37; // base๋กœ ์“ฐ์ผ ์†Œ์ˆ˜
        let total = 0;
        for (let i = 0; i < string.length; ++i) {
            total = H * total + string.charCodeAt(i);
        }
        total = total % this.table.length;
        if (total < 0) {
            total += this.table.length - 1;
        }
        return parseInt(total);
    }
    
    // hash table ๊ฐ’ ๋ณด์—ฌ์ฃผ๊ธฐ
    showDistribution() {
        let n = 0;
        for (let i = 0; i < this.table.length; ++i) {
            if (this.table[i] != undefined) {
                this.print(`${i} : ${this.table[i]}`);
            }
        }
    }

    // hash table์— ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
    put(data) {
        // let position = this.simpleHash(data); 
        let position = this.betterHash(data);
        this.table[position] = data;
    }
}

const names = ["David", "Jennifer", "Donnie", "Raymond",
"Cynthia", "Mike", "Clayton", "Danny", "Jonathan"];
const hashTable = new HashTable();
for (let i = 0; i < names.length; ++i) {
    hashTable.put(names[i]);
}
hashTable.showDistribution();

/**
10 : Mike
12 : Danny
72 : David
73 : Jonathan
88 : Clayton
92 : Raymond
104 : Donnie
109 : Jennifer
133 : Cynthia
*/

 

Hashing Integer keys

 

์ง€๊ธˆ๊นŒ์ง€๋Š” string ํƒ€์ž…์„ ํ‚ค๋กœ ๊ฐ–๋Š” hash function์„ ๋‹ค๋ค„๋ดค๋‹ค.

์ด๋ฒˆ์—๋Š” ์ •์ˆ˜ ํƒ€์ž…์„ hash ํ‚ค๋กœ ๊ฐ–๋Š” ์˜ˆ์‹œ๋ฅผ ์‚ดํŽด๋ณด์ž.

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•™์ƒ ID์™€ ์„ฑ์ ์„ ์œ„์—์„œ ์‚ดํŽด๋ดค๋˜ betterHash()์™€ simpleHash()๋กœ ๋น„๊ตํ•œ hash table์„ ๋งŒ๋“ค์–ด๋ณด์ž.

// ๋žœ๋ค ์„ฑ์ 
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}

// ํ•™์ƒ ๋ฐ์ดํ„ฐ
function generateStudentData(arr){
	for(let i = 0; i < arr.length ; ++i){
    	let num = "";
        // ํ•™์ƒ ์ ์ˆ˜ ์ƒ์„ฑ
        for(let j = 1; j <= 9; ++j){
        	num += Math.floor(Math.random() * 10);
        }
        num += getRandomInt(50, 100);
        // ํ•™์ƒ ID์— ์ ์ˆ˜ ํ• ๋‹น
        arr[i] = num;
    }
}

class HashTable {
    constructor() {
        this.table = new Array(137);
    }
    
    // console.log ํ—ฌํผ ๋ฉ”์„œ๋“œ
    print(item) {
        console.log(item);
    }
    
    // key๊ฐ€ string ํƒ€์ž…์ผ ๊ฒฝ์šฐ 
    simpleHash(data) {
        let total = 0;
        for (let i = 0; i < data.length; ++i) {
            total += data.charCodeAt(i);
        }
        this.print("Hash value: " + data + " -> " + total);
        return total % this.table.length;
    }

    // Horner's method์„ ํ™œ์šฉํ•ด ๊ฐœ์„ ํ•œ hashing function
    betterHash(string) {
        const H = 37; // base๋กœ ์“ฐ์ผ ์†Œ์ˆ˜
        let total = 0;
        for (let i = 0; i < string.length; ++i) {
            total = H * total + string.charCodeAt(i);
        }
        total = total % this.table.length;
        if (total < 0) {
            total += this.table.length - 1;
        }
        return parseInt(total);
    }

    // hash table ๊ฐ’ ๋ณด์—ฌ์ฃผ๊ธฐ
    showDistribution() {
        let n = 0;
        for (let i = 0; i < this.table.length; ++i) {
            if (this.table[i] != undefined) {
                this.print(`${i} : ${this.table[i]}`);
            }
        }
    }

    // hash table์— ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
    // put(data) {
    //     // let position = this.simpleHash(data); 
    //     let position = this.betterHash(data);
    //     this.table[position] = data;
    // }

    // ๋‘ hash function ๋น„๊ต๋ฅผ ์œ„ํ•ด type ์— ๋”ฐ๋ผ ๋ถ„๊ธฐ
	put(data,type="betterHash"){
		let position = type === "betterHash"?  this.betterHash(data): this.simpleHash(data);
        this.table[position] = data;
	}
}

let numStudents = 10;
let arrSize = 97;
let idLength = 9;
let students = new Array(numStudents);
const print = (item) =>console.log(item);
generateStudentData(students);

for(let i = 0;i<students.length; ++i){
    print(students[i].substring(0,8) + " " + students[i].substring(9));
} 

/**
//ํ•™์ƒID  ์„ฑ์ 

03393080 94
55511088 81
80924513 89
16529442 54
71582662 81
02959617 86
75489309 76
04226751 77
22656462 76
99380949 53
*/


print("\n\nData distribution: \n");

let betterHashTable = new HashTable();
let simpleHashTable = new HashTable();
for (let i = 0; i < students.length; ++i) {
   betterHashTable.put(students[i],"betterHash");
   simpleHashTable.put(students[i],"simpleHash");
    
}

print("\nbetter hash function:\n");
betterHashTable.showDistribution();
print("\nsimple hash function:\n");
simpleHashTable.showDistribution();

/**
Data distribution: 

Hash value: 03393080594 -> 572
Hash value: 55511088981 -> 579
Hash value: 80924513989 -> 586
Hash value: 16529442154 -> 571
Hash value: 71582662781 -> 581
Hash value: 02959617586 -> 586
Hash value: 75489309776 -> 593
Hash value: 04226751777 -> 576
Hash value: 22656462476 -> 578
Hash value: 99380949253 -> 589

better hash function:

0 : 03393080594
10 : 55511088981
13 : 80924513989
14 : 02959617586
15 : 04226751777
46 : 71582662781
60 : 75489309776
82 : 99380949253
108 : 22656462476
131 : 16529442154

simple hash function:

23 : 16529442154
24 : 03393080594
28 : 04226751777
30 : 22656462476
31 : 55511088981
33 : 71582662781
38 : 02959617586
41 : 99380949253
45 : 75489309776
*/

 

simple hash function์„ ์‚ดํŽด๋ณด๋ฉด ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ํ• ๋‹น๋˜์ง€ ์•Š์•˜๋‹ค. 10๋ช…์˜ ํ•™์ƒ ์ ์ˆ˜๊ฐ€ ์žˆ์ง€๋งŒ collision ๋•Œ๋ฌธ์— 9๋ช…์˜ ์ ์ˆ˜๋งŒ ํ• ๋‹น๋˜์—ˆ๋‹ค. ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ‚ค์˜ ๋ถ„ํฌ๊ฐ€ ๋ฐ€์ง‘๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฐ˜๋ฉด better hash function์€ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ํ• ๋‹น๋˜์–ด ์žˆ๊ณ  ํ‚ค๊ฐ€ ๊ณ ๋ฅด๊ฒŒ ๋ถ„๋ฐฐ๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

๋”ฐ๋ผ์„œ ํƒ€์ž…๊ณผ ๊ด€๊ณ„์—†์ด better hash function์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹๋‹ค.

Hash table์— ๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ํšŒ์ˆ˜

hash function ์ค‘ ๋” ์ข‹์€ hash ๊ฐ’์„ ๊ตฌํ•˜๋Š” ๋ฐฉ์‹์— ๋Œ€ํ•ด ์‚ดํŽด๋ดค๋‹ค.

betterhash function์„ ํ™œ์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” put ๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ํšŒ์ˆ˜ํ•˜๋Š” get ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž.

class HashTable {
    constructor() {
        this.table = new Array(137);
    }
    
    // console.log ํ—ฌํผ ๋ฉ”์„œ๋“œ
    print(item) {
        console.log(item);
    }
    
    // key๊ฐ€ string ํƒ€์ž…์ผ ๊ฒฝ์šฐ 
    simpleHash(data) {
        let total = 0;
        for (let i = 0; i < data.length; ++i) {
            total += data.charCodeAt(i);
        }
        this.print("Hash value: " + data + " -> " + total);
        return total % this.table.length;
    }

    // Horner's method์„ ํ™œ์šฉํ•ด ๊ฐœ์„ ํ•œ hashing function
    betterHash(string) {
        const H = 37; // base๋กœ ์“ฐ์ผ ์†Œ์ˆ˜
        let total = 0;
        for (let i = 0; i < string.length; ++i) {
            total = H * total + string.charCodeAt(i);
        }
        total = total % this.table.length;
        if (total < 0) {
            total += this.table.length - 1;
        }
        return parseInt(total);
    }
    
    // hash table ๊ฐ’ ๋ณด์—ฌ์ฃผ๊ธฐ
    showDistribution() {
        let n = 0;
        for (let i = 0; i < this.table.length; ++i) {
            if (this.table[i] != undefined) {
                this.print(`${i} : ${this.table[i]}`);
            }
        }
    }
    
	///////////////////////////////// ๐Ÿ’–์ถ”๊ฐ€ /////////////////////////////////
    
    // hash table์— ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
    put(data) {
        // let position = this.simpleHash(data); 
        let position = this.betterHash(data);
        this.table[position] = data;
    }

    // hash table์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
    get(key) {
        let position = this.betterHash(key);
        return this.table[position];
    }
    
    ///////////////////////////////// ๐Ÿ’–์ถ”๊ฐ€ /////////////////////////////////  
}
const print = (item) => console.log(item);
const names = ["David", "Jennifer", "Donnie", "Raymond",
"Cynthia", "Mike", "Clayton", "Danny", "Jonathan"];

const hashTable = new HashTable();

for (let i = 0; i < names.length; ++i) {
    hashTable.put(names[i]);
}
hashTable.showDistribution();

print("Retrieving values:");

for (let i = 0; i < names.length; ++i) {
    print(names[i] + " -> " + hashTable.get(names[i]));
}

/**
10 : Mike
12 : Danny
72 : David
73 : Jonathan
88 : Clayton
92 : Raymond
104 : Donnie
109 : Jennifer
133 : Cynthia
Retrieving values:
David -> David
Jennifer -> Jennifer
Donnie -> Donnie
Raymond -> Raymond
Cynthia -> Cynthia
Mike -> Mike
Clayton -> Clayton
Danny -> Danny
Jonathan -> Jonathan
*/

Collision ํ•ด๊ฒฐํ•˜๊ธฐ

hash function์ด ๋‘ ๊ฐœ ์ด์ƒ์˜ ๊ฐ’์— ๊ฐ™์€ ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๋ฉด collision (์ถฉ๋Œ)์ด ๋ฐœ์ƒํ•œ๋‹ค.

์ถฉ๋Œ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์ธ "separate chaining" ๊ณผ "linear probing"์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์ž. 

 

Separate Chaining

hash table์˜ ๊ฐ array ์—˜๋ฆฌ๋จผํŠธ๋ฅผ array์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์ €์žฅํ•˜๊ณ ,

์ถฉ๋Œ์ด ๋ฐœ์ƒํ•œ ํ‚ค์˜ array์— ๊ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ 2์ฐจ์› array ๊ตฌ์กฐ๋กœ ๋งŒ๋“ค์–ด ์ถฉ๋Œ์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

separate chaining

 

 

โ€ผ ์˜ˆ์ œ๋Š” ์ฑ…์˜ ๊ตฌํ˜„์„ ์ฐธ๊ณ ํ•˜์—ฌ ํด๋ž˜์Šค๋กœ ์žฌ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค!! ์›๋ณธ ๋ฌธ์„œ์˜ ๋ฐฐ์—ด์ด ์•„๋‹Œ linked list๋กœ ๋Œ€์ฒด ๊ตฌํ˜„ํ•ด๋ดค์Šต๋‹ˆ๋‹ค!!

์ด์ „ LinkedList๋ฅผ ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ํ™œ์šฉํ•œ separate chaining์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์ž.

class LinkedList {
    constructor() {
        this.head = null;
    }

    append(data) {
        const newNode = new Node(data);
        if (this.head === null) {
            this.head = newNode;
        } else {
            let current = this.head;
            while (current.next !== null) {
                current = current.next;
            }
            current.next = newNode;
        }
    }

    toString() {
        let current = this.head;
        let string = '';
        while (current !== null) {
            string += JSON.stringify(current.data) + ' ';
            current = current.next;
        }
        return string.trim();
    }
}

class Node {
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}

 

class HashTable {
    constructor() {
        this.table = new Array(137);
        for (let i = 0; i < this.table.length; ++i) {
            this.table[i] = new LinkedList();
        }
    }
    
    // console.log ํ—ฌํผ ๋ฉ”์„œ๋“œ
    print(item) {
        console.log(item);
    }
    
    // key๊ฐ€ string ํƒ€์ž…์ผ ๊ฒฝ์šฐ 
    simpleHash(data) {
        let total = 0;
        for (let i = 0; i < data.length; ++i) {
            total += data.charCodeAt(i);
        }
        this.print("Hash value: " + data + " -> " + total);
        return total % this.table.length;
    }

    // Horner's method์„ ํ™œ์šฉํ•ด ๊ฐœ์„ ํ•œ hashing function
    betterHash(string) {
        const H = 37; // A prime number used as the base
        let total = 0;
        for (let i = 0; i < string.length; ++i) {
            total = H * total + string.charCodeAt(i);
        }
        total = total % this.table.length;
        if (total < 0) {
            total += this.table.length - 1;
        }
        return parseInt(total);
    }
    
    // hash table ๊ฐ’ ๋ณด์—ฌ์ฃผ๊ธฐ
    showDistribution() {
        for (let i = 0; i < this.table.length; ++i) {
            if (this.table[i].head !== null) {
                this.print(`${i} : ${this.table[i].toString()}`);
            }
        }
    }

    // hash table์— ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
    put(key, value) {
        let position = this.betterHash(key);
        this.table[position].append({key, value});
    }

    // hash table์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
    get(key) {
        let position = this.betterHash(key);
        let current = this.table[position].head;
        while (current !== null) {
            if (current.data.key === key) {
                return current.data.value;
            }
            current = current.next;
        }
        return undefined;
    }
}

const print = item => console.log(item);

const names = ["David", "Jennifer", "Donnie", "Raymond",
"Cynthia", "Mike", "Clayton", "Danny", "Jonathan"];
const hashTable = new HashTable();
for (let i = 0; i < names.length; ++i) {
    hashTable.put(names[i], `Value for ${names[i]}`);
}
hashTable.showDistribution();


print("\nRetrieving values:");
for (let i = 0; i < names.length; ++i) {
    print(names[i] + " -> " + hashTable.get(names[i]));
}

/**
10 : {"key":"Mike","value":"Value for Mike"}
12 : {"key":"Danny","value":"Value for Danny"}
72 : {"key":"David","value":"Value for David"}
73 : {"key":"Jonathan","value":"Value for Jonathan"}
88 : {"key":"Clayton","value":"Value for Clayton"}
92 : {"key":"Raymond","value":"Value for Raymond"}
104 : {"key":"Donnie","value":"Value for Donnie"}
109 : {"key":"Jennifer","value":"Value for Jennifer"}
133 : {"key":"Cynthia","value":"Value for Cynthia"}

Retrieving values:
David -> Value for David
Jennifer -> Value for Jennifer
Donnie -> Value for Donnie
Raymond -> Value for Raymond
Cynthia -> Value for Cynthia
Mike -> Value for Mike
Clayton -> Value for Clayton
Danny -> Value for Danny
Jonathan -> Value for Jonathan
*/

 

Linear Probing

 

linear probing์€ open-addressing hashing์˜ ์˜ˆ์‹œ ์ค‘ ํ•˜๋‚˜๋‹ค.

์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด ํ”„๋กœ๊ทธ๋žจ์€ hash table์˜ ๋‹ค์Œ ์š”์†Œ๊ฐ€ ๋น„์–ด์žˆ๋Š” ์ง€ ํ™•์ธํ•˜๊ณ  , ๋น„์–ด ์žˆ์œผ๋ฉด ํ‚ค๋ฅผ ํ• ๋‹นํ•œ๋‹ค.

์ด ๋ฐฉ์‹์ด ๋™์ž‘ํ•˜๋ ค๋ฉด ๋นˆ ๊ณต๊ฐ„์ด ์ถฉ๋ถ„ํžˆ ์žˆ์„ ๋งŒํผ hash table์ด ์ปค์•ผ ํ•œ๋‹ค.

 

๊ทธ๋Ÿผ separate chaining๊ณผ linear probing ์ค‘ ์–ด๋–ค ๊ฑธ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ?

array ํฌ๊ธฐ๊ฐ€ ์ €์žฅํ•  ์—˜๋ฆฌ๋จผํŠธ์˜ ๊ฐœ์ˆ˜ ๋ฐ˜ ์ดํ•˜์ผ ๊ฒฝ์šฐ separate chaining์„ ์‚ฌ์šฉํ•˜๊ณ ,

array ํฌ๊ธฐ๊ฐ€ ์ €์žฅํ•  ํฌ๊ธฐ์˜ ์—˜๋ฆฌ๋จผํŠธ์˜ ๋‘ ๋ฐฐ ์ด์ƒ์ผ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” linear probing์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 

linear probing ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž.

class HashTable {
    constructor() {
        this.table = new Array(137);
        this.values = [];
    }
    
    // console.log ํ—ฌํผ ๋ฉ”์„œ๋“œ
    print(item) {
        console.log(item);
    }
    
    // Horner's method์„ ํ™œ์šฉํ•ด ๊ฐœ์„ ํ•œ hashing function
    betterHash(string) {
        const H = 37; // A prime number used as the base
        let total = 0;
        for (let i = 0; i < string.length; ++i) {
            total = H * total + string.charCodeAt(i);
        }
        total = total % this.table.length;
        if (total < 0) {
            total += this.table.length - 1;
        }
        return parseInt(total);
    }
    
    // hash table ๊ฐ’ ๋ณด์—ฌ์ฃผ๊ธฐ
    showDistribution() {
        for (let i = 0; i < this.table.length; ++i) {
            if (this.table[i] != null) {
                this.print(`${i} : ${this.table[i]}`);
            }
        }
    }

    // hash table์— ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
    put(key, data) {
        let position = this.betterHash(key);
        if(this.table[position] == undefined){
			this.table[position] = key;
            this.values[position] = data;
        } else {
        	while(this.table[position] != undefined){
            	position++;
            }
            this.table[position] = key;
            this.values[position] = data;
        }
    }

    // hash table์˜ hash ์œ„์น˜์—์„œ ํ‚ค๊ฐ€ ์ผ์น˜ํ•˜๋ฉด ์œ„์น˜์— ํ• ๋‹นํ•˜๊ณ , 
    // ์•„๋‹ˆ๋ผ๋ฉด ํ• ๋‹น๋˜์ง€ ์•Š์€ ์œ„์น˜(undefined)๊ฐ€ ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ ๋ฃจํ”„๋ฅผ ๋Œ๋‹ค๊ฐ€ ํ• ๋‹น์„ ํ•œ๋‹ค.
    get(key) {
        let hash = -1;
        hash = this.betterHash(key); 
        if (hash > -1) {
        	for (let i = hash; this.table[hash] != undefined; i++) { 
            	if (this.table[hash] == key) {
        			return this.values[hash]; 
                }
    		} 
        }
        return undefined; 
        }
}


const print = item => console.log(item);

const names = ["David", "Jennifer", "Donnie", "Raymond",
"Cynthia", "Mike", "Clayton", "Danny", "Jonathan"];
const hashTable = new HashTable();
for (let i = 0; i < names.length; ++i) {
    hashTable.put(names[i], `Value for ${names[i]}`);
}
hashTable.showDistribution();


print("\nRetrieving values:");
for (let i = 0; i < names.length; ++i) {
    print(names[i] + " -> " + hashTable.get(names[i]));
}

/**
10 : Mike
12 : Danny
72 : David
73 : Jonathan
88 : Clayton
92 : Raymond
104 : Donnie
109 : Jennifer
133 : Cynthia
 
Retrieving values:
David -> Value for David
Jennifer -> Value for Jennifer
Donnie -> Value for Donnie
Raymond -> Value for Raymond
Cynthia -> Value for Cynthia
Mike -> Value for Mike
Clayton -> Value for Clayton
Danny -> Value for Danny
Jonathan -> Value for Jonathan
*/

 


๐Ÿ“š Reference

- Data Structures and Algorithms Using Javaโ€ Script by Michael McMillian (O’Reilly). Copyright 2014 Michael McMillan, 978-1-449-36493-9