他のポスターは、文字を素数に変換し、それらを乗算することを提案しました。これを大きな素数で行うと、オーバーフローしない優れたハッシュ関数が得られます。ほとんどの英単語の Unix 単語リストに対して次の Ruby コードをテストしたところ、相互のアナグラムではない単語間にハッシュ衝突は見つかりませんでした。(MAC OS X では、このファイルは /usr/share/dict/words にあります。)
私の word_hash 関数は、各文字の mod 32 の序数値を取ります。これにより、大文字と小文字が同じコードを持つようになります。私が使用する大きな素数は 2^58 - 27 です。2^64 / A 未満であれば、どんな大きな素数でも構いません。ここで、A はアルファベットのサイズです。私はアルファベットのサイズとして 32 を使用しているので、これは約 2^59 - 1 より大きい数値を使用できないことを意味します。Ruby は符号に 1 ビットを使用し、値が数値かオブジェクトかを示すために 2 番目のビットを使用するためです。 、私は他の言語に少し負けています。
def word_hash(w)
# 32 prime numbers so we can use x.ord % 32. Doing this, 'A' and 'a' get the same hash value, 'B' matches 'b', etc for all the upper and lower cased characters.
# Punctuation gets assigned values that overlap the letters, but we don't care about that much.
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131]
# Use a large prime number as modulus. It must be small enough so that it will not overflow if multiplied by 32 (2^5). 2^64 / 2^5 equals 2^59, so we go a little lower.
prime_modulus = (1 << 58) - 27
h = w.chars.reduce(1) { |memo,letter| memo * primes[letter.ord % 32] % prime_modulus; }
end
words = (IO.readlines "/usr/share/dict/words").map{|word| word.downcase.chomp}.uniq
wordcount = words.size
anagramcount = words.map { |w| w.chars.sort.join }.uniq.count
whash = {}
inverse_hash = {}
words.each do |w|
h = word_hash(w)
whash[w] = h
x = inverse_hash[h]
if x && x.each_char.sort.join != w.each_char.sort.join
puts "Collision between #{w} and #{x}"
else
inverse_hash[h] = w
end
end
hashcount = whash.values.uniq.size
puts "Unique words (ignoring capitalization) = #{wordcount}. Unique anagrams = #{anagramcount}. Unique hash values = #{hashcount}."