頻繁に追加・削除される大量のデータから、ある文字列をキーにして目的の値を素早く取り出したい場合に有効。
以上を実装する必要がある。
def makeHash1(str, hashmax) hash = 0 n = 0 str.each_byte{|byte| hash += byte } return hash % hashmax end
def makeHash2(str, hashmax) hash = 0 weight = 0 str.each_byte do |byte| if weight > 7 weight = 0 end hash += byte << (4 * weight) weight += 1 end return hash % hashmax end
puts "makeHash1を使った場合" p makeHash1("this", 100) # => 40 p makeHash1("itsh", 100) # => 40 puts "makeHash2を使った場合" p makeHash2("this", 100) # => 0 p makeHash2("itsh", 100) # => 85
ハッシュ表のサイズは素数にするのが一般的。よく使われる値としては、
「89, 179, 503, 1009, 2003, 3001, 4001, 5003, 10007」
しかし、それでも重複を完全に避けられるわけではない。対策としては、
class WordSet attr_reader :english, :japanese def initialize( english, japanese ) @english = english @japanese = japanese end end class HashTable attr_accessor :data, :size def initialize @data = Array.new @size = nil end end class Dictionary def main() words = [ WordSet.new("dolphin", "イルカ"), WordSet.new("beluga", "シロイルカ"), WordSet.new("grampus", "シャチ"), WordSet.new("medusa", "海月") ] hashTable = HashTable.new initHashTable(hashTable, 503) words.each do |word| addDataToMap(hashTable, word) end action = nil while action != 0 p hashTable.data.size p hashTable puts "1:検索 2:削除 3:全表示 0:終了" action = gets.chomp.to_i case action when 1 puts "検索する英単語を入力して下さい" key = gets.chomp japanese = getDataFromMap(hashTable, key) if japanese != nil puts "#{key} の和訳は 「#{japanese}」です。" else puts "#{key}が見つからなかった" end when 2 puts "削除する英単語を入力して下さい" key = gets.chomp wordFound = deleteDataFromMap(hashTable, key) if wordFound != nil puts "#{key}をマップから削除しました" else puts "#{key}が見つからなかった" end when 3 printAllData(hashTable) end end end private def makeHash2( str, hashMax ) hash = 0 weight = 0 str.each_byte do |byte| if weight > 7 weight = 0 end hash += byte << (4 * weight) weight += 1 end return hash % hashMax end def reHash( hashTable, firstHash ) hashValue = firstHash # firstValue から k^2 だけ後ろにある空き位置を探す # 「k > ハッシュ表サイズの半分」となったら、それ以降の探索は無駄 k = 1 while k < hashTable.size / 2 if hashTable.data[hashValue] != nil return hashValue end k += 1 end # 空き位置が見つからなければ -1 を返す return -1 end def addDataToMap( hashTable, newData ) # 英単語を元にハッシュ値を生成 hashValue = makeHash2(newData.english, hashTable.size) # hash の位置がすでに埋まっていたら、再ハッシュを行う if hashTable.data[hashValue] != nil hashValue = reHash(hashTable, hashValue) if hashValue == -1 puts "#{newData.english} をマップに挿入しようと試みましたが、空き位置がありませんでした" return end end # hashValue の位置に newData を格納 hashTable.data[hashValue] = newData end def getDataFromMap( hashTable, key ) # 探索を開始するハッシュ値を求める hashValue = makeHash2(key, hashTable.size) word = nil # 探索 k = 0 while k <= hashTable.size / 2 word = hashTable.data[(hashValue + k * k) % hashTable.size] if word != nil if key == word.english return word.japanese end end k += 1 end return nil end def deleteDataFromMap( hashTable, key ) # 探索を開始するハッシュ値を求める hashValue = makeHash2(key, hashTable.size) word = nil k = 0 while k < hashTable.size / 2 word = hashTable.data[(hashValue + k * k) % hashTable.size] if word != nil if key == word.english hashTable.data[(hashValue + k * k) % hashTable.size] = nil return word end end k += 1 end return nil end def initHashTable( hashTable, size ) i = 0 while i < size hashTable.data[i] = nil i += 1 end hashTable.size = size end def printAllData( hashTable ) n = 0 while n < hashTable.size if hashTable.data[n] != nil puts "#{n} :¥t#{hashTable.data[n].english}¥t#{hashTable.data[n].japanese}" end n += 1 end end end dictionary = Dictionary.new dictionary.main