せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】【アルゴリズム】ハッシュテーブルの実装。衝突回避あり(一次団子現象回避Ver)

 

ハッシュテーブルとは

ハッシュテーブル (hash table) は、キーと値の組(エントリと呼ぶ)を複数個格納し、キーに対応する値をすばやく参照するためのデータ構造。ハッシュ表ともいう。

http://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB

あえて、ハッシュ値が衝突するデータを追加していく。

ハッシュ値計算方法

  def _make_hash(str)
    size = self.array.size
    result = 0
    str.bytes.to_a.each_with_index do |ch, idx|
      result += ch << (4* (idx % 8))
    end
    result = result % size
  end

 
(例)

"abc" はアスキーコード[65, 66, 67]。
よって、(65*16**0 + 66*16**1 +67*16**2 ) %16 で 1

 
テーブルサイズを16にすることで、先頭以外の文字はhash値に影響しない。
言い換えると、"abc"も"a"も"aka"も全部 hash値が1なので、衝突しやすい。


 

作成したコード

class MyData
  attr_accessor :english, :japanese
  def initialize(english, japanese)
    @english = english
    @japanese = japanese
  end
  
end

class HashMap
  attr_accessor :array
  def initialize(size)
    @array = Array.new(size)
  end
  def clear
    @array.clear
  end

  #ハッシュ値を計算
  def _make_hash(str)
    size = self.array.size
    result = 0
    str.bytes.to_a.each_with_index do |ch, idx|
      result += ch << (4* (idx % 8))
    end
    result = result % size
  end
  def re_hash(hash)
    k =1
    max =  Math.sqrt(self.array.size).ceil
    while k < max do
      return hash + k*k if self.array[hash + k*k].nil?
      k += 1
    end
    -1
  end
  def add(data)
    hash = _make_hash(data.english)
    return self.array[hash] = data if self.array[hash].nil?
    hash = re_hash(hash)
    raise "mou ippai" if hash == -1    
    return self.array[hash] = data
  end

  def search(str)
    hash = _make_hash(str)
    return "naiyo" if self.array[hash].nil?
    k =0
    max =  Math.sqrt(self.array.size).ceil
    while k < max do
      data = self.array[hash + k*k]
      return data.japanese if data.english == str
      k += 1
    end
    "naiyo"
  end

  # pメソッド表示用
  def inspect
    self.array.delete(nil)
    self.array
  end
end

def dump(hash_map)
  p hash_map.array
end

hash_map = HashMap.new(16)
#ハッシュ値が同じ(0)データを追加する
hash_map.add(MyData.new("pen","enpitsu"))
hash_map.add(MyData.new("people","hitobito"))
hash_map.add(MyData.new("popular","ninki"))
hash_map.add(MyData.new("pop","karoyaka"))

dump(hash_map) #値が0,1,4,9 番目に格納されている
p hash_map

p hash_map.search("udon")
p hash_map.search("pokopen")

hash_map.add(MyData.new("poor","shishou")) #衝突によるエラー

 

実行結果

"p"はhash値が0。

[#<MyData:0x2b28fd0 @english="pen", @japanese="enpitsu">, #<MyData:0x2b28f40 @english="people", @japanese="hitobito">, nil, nil, #<MyData:0x2b28e80 @english="popular", @japanese="ninki">, nil, nil, nil, nil, #<MyData:0x2b28d48 @english="pop", @japanese="karoyaka">, nil, nil, nil, nil, nil, nil]
[#<MyData:0x2c78f58 @english="pen", @japanese="enpitsu">, #<MyData:0x2c78ec8 @english="people", @japanese="hitobito">, #<MyData:0x2c78e08 @english="popular", @japanese="ninki">, #<MyData:0x2c78cd0 @english="pop", @japanese="karoyaka">]
"naiyo"
"naiyo"
C:/h1.rb:43:in `add': mou ippai (RuntimeError)


ここでHashクラスの実装が見れるが良く分からず。