简单流行算法

时间:2012-06-20 20:57:54

标签: algorithm popularity

根据项目的年龄以及项目收到的投票,点击次数或购买次数,有大量suggested algorithms用于计算受欢迎程度。但是,我看到的更健壮的方法通常需要过于复杂的计算和多个存储的值,这会使数据库变得混乱。我一直在考虑一种非常简单的算法,它不需要存储任何变量(除了流行度值本身)并且只需要一个简单计算。这太荒谬了:

p = (p + t) / 2

此处, p是存储在数据库中的受欢迎度值 t是当前时间戳。首次创建项目时,必须初始化 p 。有两种可能的初始化方法:

  1. 使用当前时间戳 t
  2. 初始化 p
  3. 使用数据库中所有 p 值的平均值初始化 p
  4. 请注意,初始化方法(1)为最近添加的项目提供了优于历史项目的明显优势,从而添加了相关性元素。另一方面,初始化方法(2)在与历史项目进行比较时将新项目视为等于。

    假设您使用初始化方法(1)并使用当前时间戳初始化 p 。当项目收到第一次投票时, p 成为创建时间和投票时间的平均值。因此,流行度值 p 仍然表示有效的时间戳(假设您舍入到最接近的整数),但它表示的实际时间是抽象的。

    使用此方法,只需要一个简单的计算,并且只需要在数据库中存储一个值( p )。此方法还可以防止失控值,因为给定项目的受欢迎程度永远不会超过当前时间。

    在1天内工作的算法示例:http://jsfiddle.net/q2UCn/
    该算法在1年期间工作的示例:http://jsfiddle.net/tWU9y/

    如果您希望投票以亚秒级间隔稳定地流入,那么您将需要使用微秒时间戳,例如PHP microtime()函数。否则,标准UNIX时间戳将起作用,例如PHP time()函数。

    现在我的问题是:你认为这种方法有什么重大缺陷吗?

5 个答案:

答案 0 :(得分:9)

我认为这是一种非常好的方法,因为它很简单。一个非常有趣的结果。

我做了一个快速的计算,发现这个算法似乎确实理解了“流行度”的含义。它的问题在于它明显倾向于支持这样的近期投票:

想象一下,我们花时间把它分成100到1000之间的离散时间戳值。假设在t = 100时,A和B(两个项目)都有相同的P = 100.

    A gets voted 7 times on 200, 300, 400, 500, 600, 700 and 800
resulting on a final Pa(800) = 700 (aprox).

    B gets voted 4 times on 300, 500, 700 and 900 
resulting on a final Pb(900) = 712 (aprox).

当t = 1000时,A和B都会收到投票,所以:

Pa(1000) = 850 with 8 votes
Pb(1000) = 856 with 5 votes

为什么呢?因为该算法允许项目快速击败历史领导者,如果它收到更多近期投票(即使该项目的总票数较少)。

编辑包括模拟

OP创造了一个很好的小提琴,我改变了以获得以下结果:

http://jsfiddle.net/wBV2c/6/

Item A receives one vote each day from 1970 till 2012 (15339 votes)
Item B receives one vote each month from Jan to Jul 2012 (7 votes)

The result: B is more popular than A.

答案 1 :(得分:3)

我看到一个问题,只有最后的~24票数。

p_i+1 = (p + t) / 2

我们有两票

p2 = (p1 + t2) / 2 = ((p0 + t1) /2 + t2 ) / 2 = p0/4 + t1/4 + t2/2

扩大32票的范围给出:

p32 = t*2^-32 + t0*2^-32 + t1*2^-31 + t2*2^-30 + ... + t31*2^-1

因此,对于带符号的32位值,t0对结果没有影响。因为t0除以2 ^ 32,所以它对p32没有贡献。

如果我们有两个项目A和B(无论差异有多大),如果他们都获得相同的32票,他们将具有相同的受欢迎程度。所以你的历史可以追溯到32票。如果最后32票相同,则2032和32票没有区别。

如果差异小于一天,则在17票之后它们将相等。

答案 2 :(得分:2)

提出的算法是一种很好的方法,并且是Exponential Moving Average的特殊情况,其中alpha = 0.5:

<div class="test-sticker">
  <div class="wrapper">
    <div class="sticker">
      <div class="reveal circle_wrapper">
        <div class="circle"></div>
      </div>

      <div class="sticky">
        <div class="front circle_wrapper">
          <div class="circle"></div>
        </div>
      </div>

      <div class="sticky anim750">
        <div class="back circle_wrapper anim750">
          <div class="circle anim750"></div>
        </div>
      </div>

    </div>
  </div>
</div>

调整以下事实的一种方法:建议的alpha = 0.5解决方案倾向于偏爱最近的投票(如daniloquio所指出的)是为alpha选择较高的值(例如0.9或0.99)。请注意,将其应用于daniloquio提出的测试用例不起作用,但是,因为当alpha增加时,算法需要更多的“时间”来解决(因此数组应该更长,这在实际应用中通常是正确的) )。

因此:

  • 对于alpha = 0.9,该算法平均会计算最后10个值
  • 对于alpha = 0.99,该算法平均会计算最近100个值
  • 对于alpha = 0.999,该算法平均会计算最近1000个值

答案 3 :(得分:1)

缺点是100票的东西通常比最近只有一票的东西更有意义。但是,如果你的方案的变体能够合理地运行起来,那就不难了。

答案 4 :(得分:0)

我不认为上面讨论的逻辑会起作用。 p_i + 1 =(p_i + t)/ 2

在时间戳上查看文章A:70,80,90受欢迎程度(文章A):82.5

B条按时间戳查看:50,60,70,80,90受欢迎程度(文章B):80.625

在这种情况下,B条的受欢迎程度应该更高。首先,B条最近被视为A条,其次,它也被视为比A条更多次。