ユークリッド距離で計算。
まずはデータソース
val source = Map( "azalea" -> Map( "Java" -> 5, "C#" -> 5, "Python" -> 2, "Perl" -> 2, "Scala" -> 5, "C" -> 4, "C++" -> 4 ), "nullpobug" -> Map( "Ruby" -> 4, "Python" -> 5, "Java" -> 4, "PHP" -> 4, "Delphi" -> 5, "C++" -> 4 ), "delick" -> Map( "Java" -> 5, "C++" -> 3 ) )
で、ここから二者間で共通値を検索する
def findCommonItem(left:Map[String, Int], right:Map[String, Int]):Iterable[(Int, Int)] = { left.filter(kv => { val (key, _) = kv right.contains(key) }).map(kv => { val (key, value) = kv (value, right(key)) }) } val commonValue = findCommonItem(source("azalea"), source("nullpobug"))
そしたらそのセットの距離を計算する
def distance(values:Iterable[(Int, Int)]) = { def total = values.foldLeft(0.0){case (value, (left, right)) => { value + Math.pow(left - right, 2) }} Math.sqrt(total) } val distance = distance(commonValue)
距離が短いほど二者間の距離が近いことになる。が、類似度を計算するならその逆数が必要な筈なので。
1.0 / (1.0 + distance(commonValue))
commonValue のセットがそもそも 0 だと成り立たないのでその時は 0 を返す。
def exec(left:Map[String, Int], right:Map[String, Int]) = { val commonValue = findCommonItem(left, right) if (commonValue.isEmpty) { 0.0 } else { 1.0 / (1.0 + distance(commonValue)) } }
が、これでも欠点はある。 上記なんかが良い例で、一致するサンプル数が少ないと正確に比較ができない。