なんじゃくにっき

プログラミングの話題中心。

集合知 in Scala (1)

 オライリー集合知プログラミングを買ってきた。
理解を深めるために、Scalaでコードを書きながら読み進めようと思う。
 
 まずは、ユークリッド距離によるスコア。
要はn次元ベクトル2つを比較した際に、
2つのn次元ベクトルを(a1, a2 .... an-1, an), (b1, b2, ... bn-1, bn)と表したとき、
各要素の差の2乗の和、(a1 - b1) ^ 2 + (a2 - b2) ^ 2 + ... + (an - bn) ^ 2を2つのベクトル間の距離とする考え方。
 
 これをもとに、あるデータが与えられたときの2つのものの間の類似度を求めてみる。
ここではTaro, Hanako, Jiroが登場し、それぞれが各果物へのpreference(嗜好)を持つとする。
preferenceは1から10でランク付けられる。
2人の間の嗜好の類似度は、(1 - (1 / 嗜好のユークリッド距離))で表され、
完全に嗜好が一致したときスコアは1、完全に違う場合は0。
 
っていうのをScalaで書いてみた。
まあ余り効率もよくないプログラムだけど、理解のため、理解のため。

object Hoge extends App {
// 2人の嗜好の類似度を返す
def getSimilality(prefs: Map[String, Map[String, Int] ],
person1: String, person2: String): Double = {

val map1 = prefs(person1)
val map2 = prefs(person2)
val intersection = map1.keys.toSet.intersect(map2.keys.toSet) // 共通キー

// 共通するキーがなければ類似度は0
if (intersection.size == 0) {
return 0
}

val distance = intersection.map(s => Math.pow(map1(s) - map2(s), 2.0)).sum
1 / (1 + distance)
}

val preferences = Map("Taro" -> Map("apple" -> 6,
"orange" -> 5,
"banana" -> 10),
"Hanako" -> Map("apple" -> 1,
"orange" -> 10,
"banana" -> 3),
"Jiro" -> Map("grape" -> 5)
)

println(getSimilality(preferences, "Taro", "Jiro"))
}
 
 
おまけ
SQLで書いてみる。
person, fruit, scoreというカラムを持つpreferenceテーブルがあるとすると(person, fruitが主キー)、
TaroとJiroの類似度は
select 1 / (1 + sum(pow(p1.score - p2.score, 2)))
from preference as p1
join preference as p2 on (p1.fruit = p2.fruit)
where p1.person = 'Taro'
and p2.person = 'Jiro'