Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Reddit 最新热度排名算法分析

#source code

cdef extern from "math.h":
    double log10(double)
    double sqrt(double)

epoch = datetime(1970, 1, 1, tzinfo = g.tz)

cpdef double epoch_seconds(date):
    """Returns the number of seconds from the epoch to date. Should
       match the number returned by the equivalent function in
       postgres."""
    td = date - epoch
    return td.days * 86400 + td.seconds + (float(td.microseconds) / 1000000)

cpdef long score(long ups, long downs):
    return ups - downs

cpdef double hot(long ups, long downs, date):
    return _hot(ups, downs, epoch_seconds(date))

cpdef double _hot(long ups, long downs, double date):
    """The hot formula. Should match the equivalent function in postgres."""
    s = score(ups, downs)
    order = log10(max(abs(s), 1))
    if s > 0:
        sign = 1
    elif s < 0:
        sign = -1
    else:
        sign = 0
    seconds = date - 1134028003
    return round(sign * order + seconds / 45000, 7)

#analysis

cdef extern from "math.h":
    double log10(double)
    double sqrt(double)

从外部(math.h)引入两个数学函数

epoch = datetime(1970, 1, 1, tzinfo = g.tz)

定义时间基准为UTC标准起始时间

cpdef double epoch_seconds(date):
    """Returns the number of seconds from the epoch to date. Should
       match the number returned by the equivalent function in
       postgres."""
    td = date - epoch
    return td.days * 86400 + td.seconds + (float(td.microseconds) / 1000000)

如代码中注释所示,返回从时间纪元起始到给定时间的秒数 td为两个时间对象相减产生的时间间隔(timedelta)对象,时间间隔对象只在内部存储: , , 微秒 . 其中:86400 表示一天(24小时)的总秒数,微秒为百万分之一秒。

cpdef long score(long ups, long downs):
    return ups - downs

返回赞同数与反对数的差值

cpdef double hot(long ups, long downs, date):
    return _hot(ups, downs, epoch_seconds(date))

计算热度的 major method ,接收三个参数:赞同数、反对数、当前时间(内部通过调用epoch_seconds方法将当前时间换算为距离时间纪元的秒数)

cpdef double _hot(long ups, long downs, double date):
    """The hot formula. Should match the equivalent function in postgres."""
    s = score(ups, downs)
    order = log10(max(abs(s), 1))
    if s > 0:
        sign = 1
    elif s < 0:
        sign = -1
    else:
        sign = 0
    seconds = date - 1134028003
    return round(sign * order + seconds / 45000, 7)

计算热度的公式,其中:

s :得分,赞同数与反对数的差值

order :分解如下

abs(s) :得分的绝对值,忽略正负,下面简称为 x

max(x,1) :如果赞同数等于反对数,则结果为1,否则为x,对其结果下面简称为 y

log10(y) :此处为什么要选择进行log运算,目的是为了 平缓 跟风投票/后续投票的 影响力 (有些权重的意思) ,例如此处y为1,结果为0;y为10,结果为1;y为100,结果为2;y为1000,结果为3;投前10票的人,跟后面跟风投99票的人影响力等同。这在某种程度上对恶意刷排名有一定的抑制作用。对于该结果,下面简称为 z

sign :是一个标识,用于判断,该帖子的整体的趋势是被赞成还是被反对的,它很重要,因为在下面它直接决定了,它是评判热度的加分项还是减分项!

seconds :得出发帖时间,上面已经提到,这里的date变量存储的是发帖时间距离时间纪元的秒数,而此处的 1134028003 换算成日期时间为: 2005年12月8日7:46:43 对于该特殊时间的解释,网传是Reddit成立的时间,由此可见,seconds又是个相对时间的秒数,可以理解为相对发帖时间

round(sign * order + seconds / 45000, 7) :分解如下

sign * order :根据赞成数与反对数来计算得分,上面以及解释过,两者的差额越大,则order越高。但光凭差额还不够,需要看用户的投票趋势或方向(这是非常重要的)。因此sign直接影响热度(如果用户整体呈反对趋势,不认为它热度很高)。

seconds / 45000 :表示发帖时间越长,其排名越往下降,45000为12.5小时的秒数(即为半天),注意,seconds是一个相对时间,如果发帖时间越早,其越小,导致该值也就越小

round(sign * order + seconds / 45000, 7) :保留小数点后的7位

总结:Reddit上帖子的热度,主要依赖两个指标:赞成与反对的投票方向与投票差、发帖时间的早晚。这是Reddit帖子热度计算的最新公式,而之前的公式是:查看commit

round(order + sign * seconds /45000, 7)

可以看出就在前不久提交的修改,提交人员给出的comment为: Make hot score continuous. 从修改中,确实可以看出,这是为了保持帖子在同一投票方向以及时间上的连续性。 sign 原本就是根据赞同/反对得出的投票方向,应该将其的影响施加在跟投票相关的评比项上,而不应该跟影响到时间的评比。

源码地址:https://github.com/reddit/reddit/blob/master/r2/r2/lib/db/_sorts.pyx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment