ネットの海の片隅で

技術ネタの放流、あるいは不法投棄。

任意のビット列を0以上1以下の数値に変換する

やりたかったこと

  • 文字列に対して0以上1以下の数値を割り当てる
  • 複数回実行した場合でも割り当てる数値は常に等しい

2つ目の要件がなければ乱数を取ってきて割り当てれば良いのだが、再現性が必要なため乱数は使えない。 したがって、文字列をもとに一見すると乱数のように見える数値を算出する必要がある。

言い換えれば、任意のビット列から[0, 1]への一様性がある写像を考えるということになると思う。

方法

元データからハッシュを求め、それを小数に変換する。

require "digest/sha1" # ハッシュ関数はSHA1じゃなくても良い

def convert(arg)
  hex_digest = Digest::SHA1.hexdigest(arg.to_s)
  hex_digest.to_i(16).to_f / 16 ** hex_digest.size
end

convert(1) # => 0.20865018187858086
convert(2) # => 0.8527156244334038
convert(3) # => 0.4682374510274206

結果

とりあえず、1から1000までの数値を文字列にして試してみた。

各文字列はどういった値になったか

横軸は引数とした数値文字列"#{n}"、縦軸は結果として得られた数値。

f:id:s_osa:20141228192824p:plain

ヒストグラム

級数は20。

f:id:s_osa:20141228193310p:plain

結論

雑な検証ではあるが、ある程度使えそうなことはわかった。

得られる数値が乱数っぽいかどうかはハッシュ関数の一様性に依存するので、一様性を気にする場合はハッシュ関数の選択に気をつける必要がある。

参考:The distribution of hash function outputs

Special Thanks