我正在设计一种比较两个物体的算法,我有一个公式,但我不知道它是否尽可能好。
essentialy,我正在比较两场比赛之间的比喻,说明它们有多相似:
$divisor = ((count($similar_concepts) - $iterator) + ($total - $iterator) + ($iterator));
echo "<BR> Value: ".($iterator / $divisor);
但是,这不可读,所以这就是:
SimilarTropes/( (OriginalTropes - SimilarTropes) + (NewTropes - SimilarTropes) + (SimilarTropes) )
我对结果并不完全满意,这是一个例子:
Similarities: 47
NewTropes: 107
OriginalTropes: 156
Answer: 0.21759259259259
我不喜欢这些结果,因为我觉得这些数字应该具有更高的相似性百分比。
我喜欢这里的一些意见,如果我在错误的地方,至少应该指导我应该去哪里。
非常感谢!
答案 0 :(得分:5)
让我(尝试)将你所拥有的东西翻译成更具数学公式的东西。从那里开始应该会更容易。
OriginalTropes
是某些游戏中的转义数,称之为A
。然后NewTropes
是来自其他游戏的转义,称之为B
。然后,Similarities
只是A
和B
的交集。那么你的公式是:
|Intersect(A, B)| / ((|A| - |Intersect(A, B)|) + (|B| - |Intersect(A, B)|) + |Intersect(A, B)|)
简化,我们有:
|Intersect(A, B)| / (|A| + |B| - |Intersect(A, B)|)
换句话说,你说的是相似度是公共物品数量除以物品总数减去共同物品数量之间的比率。
现在让我们来看几个特例。拿A = B
。然后我们有:
|Intersect(A, B)| = |A| = |B|
。那么你的公式是:
|A| / (|A| + |A| - |A|) = 1
现在假设集合A
和B
的大小相等。但是,他们只有一半的项目是共同的。换句话说,
|A| = |B| = 2 |Intersect(A, B)|
你的相似度得分是:
1/2 |A| / (2|A| - 1/2|A|) = 1/3
理想情况下,这应该是1/2
,而不是1/3
。如果您考虑|A| = |B| = n
和|Intersect(A, B)| = n * p
0 <= p <= 1
的任何集合,您会得到相似的内容。
一般情况下,对于上述形式的集合,最终会使用相似度算法低估两组之间的相似性。这看起来像下图中的紫色曲线。蓝色曲线是余弦相似性所给出的。因此,如果50%是常见且它们大小相等,则这两组具有0.5
的相似性。同样,如果他们有90%的共同点,则它具有0.9
的相似性。
您可能希望的是两组之间的角度。考虑总元素集Intersect(A, B)
和定义N = |Intersect(A, B)|
。设a
和b
为N
和A
的{{1}}维表示,其中每个元素的值为B
(如果原始集合中存在)或1
,如果没有。
然后使用角度的余弦作为:
0
请注意,符号Cos(theta) = Dot(a, b) / (||a|| * ||b||)
指的是欧几里德长度,而不是集合的大小。这可能比您以前使用的更好。
这是一个例子。让我们说:
||a||
然后完整的 distinct 集, A = { "Big Swords", "Male Hero", "No Cars" }
B = { "Male Hero", "Trains", "No Dragons" }
给出为:
Union(A, B)
这意味着Union(A, B) = { "Big Swords", "Male Hero", "No Cars", "Trains", "No Dragons" }
。棘手的一方成为如何恰当地索引这些中的每一个。您实际上可以使用字典和计数器来索引元素。我会留给你试试看。现在,我们将使用N = |Union(A, B) = 5
的顺序。然后Union(A, B)
和a
被命名为:
b
此时它成为标准数学:
a = { 1, 1, 1, 0, 0 }
b = { 0, 1, 0, 1, 1 ]
Dot(a, b) = 1
|a| = sqrt(3)
|b| = sqrt(3)
Similarity = 1 / 3