惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
S
Secure Thoughts
月光博客
月光博客
美团技术团队
雷峰网
雷峰网
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
News and Events Feed by Topic
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Forbes - Security
Forbes - Security
W
WeLiveSecurity
P
Proofpoint News Feed
阮一峰的网络日志
阮一峰的网络日志
爱范儿
爱范儿
G
GRAHAM CLULEY
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
AI
AI
Last Week in AI
Last Week in AI
Google Online Security Blog
Google Online Security Blog
Schneier on Security
Schneier on Security
云风的 BLOG
云风的 BLOG
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Recent Announcements
Recent Announcements
Webroot Blog
Webroot Blog
T
Tor Project blog
Cisco Talos Blog
Cisco Talos Blog
N
News and Events Feed by Topic
罗磊的独立博客
The Register - Security
The Register - Security
Blog — PlanetScale
Blog — PlanetScale
T
Threat Research - Cisco Blogs
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
T
The Exploit Database - CXSecurity.com
www.infosecurity-magazine.com
www.infosecurity-magazine.com
B
Blog
腾讯CDC
Microsoft Azure Blog
Microsoft Azure Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Engineering at Meta
Engineering at Meta
Latest news
Latest news
IT之家
IT之家
D
DataBreaches.Net
博客园 - 司徒正美
N
Netflix TechBlog - Medium
V
V2EX
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

Evan Miller’s News

Likelihood-ratio inference on differences in quantiles Attention Is Off By One Formulas for Bootstrapping Sample Medians You Can’t Spell CUPED Without Frisch-Waugh-Lovell The Floppy Disk of Floating Point Formulas for Bayesian A/B Testing SlowerLogLog – Evan Miller Preface to A Lost Lady – Evan Miller Preface to Brave New World – Evan Miller Preface to The Time Machine – Evan Miller Preface to Frankenstein – Evan Miller A Stochastic Valuation Model – Evan Miller Things That Bother Me About Swift – Evan Miller Things That Bother Me About macOS – Evan Miller A Review of Perl 6 (Raku) – Evan Miller Why I’m Learning Perl 6 – Evan Miller Adventure Games and Eigenvalues – Evan Miller Microsoft Surface Studio and Giant Apple iPads – Evan Miller Type Punning Functions in C – Evan Miller Elixir RAM and the Template of Doom – Evan Miller Splatoon’s Ranking System Is Still Broken – Evan Miller Simple Sequential A/B Testing – Evan Miller Evaluating Splatoon’s Ranking System – Evan Miller Inferring Tweet Quality From Retweets – Evan Miller Ranking News Items With Upvotes – Evan Miller A Taste of Rust Four Days of Go – Evan Miller
Deriving the Reddit Formula – Evan Miller
Evan Miller · 2015-07-14 · via Evan Miller’s News

By Evan Miller

July 13, 2015

A few things about Reddit’s hot formula have always bothered me. The formula has obviously been a success when it comes to setting the Internet on fire, but I have to wonder:

  1. Where do the seemingly arbitrary constants come from, and how do they effect the rankings?

  2. Why doesn’t the current time appear in the calculation?

  3. Why is there a logarithm?

  4. What’s with taking the absolute value of s?

I decided I would try to derive a “hot” formula from expected-utility theory to see if I could shed any light on the Reddit formula. Much to my surprise, the formula I came up with is remarkably similar to Reddit’s. It explains the origin of the strange constants, the presence of the logarithm, and the absence of the current time. One interesting feature of the Reddit formula is that, according to my analysis, the formula is optimized for users who visit the site every 5.43 hours. (Who knew?)

The derivation also points to an expression I believe is missing from the Reddit formula, one that would help bring highly-rated items to the top. At the end of this post I’ll propose an amendment to the existing formula — which, before you get too excited, comes with important caveats — but in the meantime, let’s dig into some math.

Reddit Utility Theory

Suppose a Reddit visitor loads the Front Page of the Internet, on average, every \(S\) seconds. Let’s assume that a given Reddit visitor reloads the page basically at random, but maintains an average reload rate throughout the day of \(\lambda = 1/S\). (If \(S=120\), the reload rate is \(\lambda = 1/120\).)

When a Reddit visitor sees a story, she might have four reactions:

  1. It’s a new story that she likes
  2. It’s a new story that she dislikes
  3. It’s an old (previously seen) story that she likes
  4. It’s an old story that she dislikes

Let’s associate a utility payoff with each of these scenarios:

  1. \(a = \) It’s a new story that she likes
  2. \(b = \) It’s a new story that she dislikes
  3. \(c = \) It’s an old (previously seen) story that she likes
  4. \(d = \) It’s an old story that she dislikes

To determine the relative probability of these four events, we need to know two things:

  1. What’s the probability that a story will be liked?
  2. What’s the probability that a story is new (to a random visitor)?

For now let’s call these two probabilities \(p\) (probability a story is liked) and \(q\) (probability a story is new). To make things easier we can make a table of the four possible events with their utility payoffs and probabilities:

Story is liked?Story is new?ProbabilityPayoff
LikedNew\(pq\)\(a\)
DislikedNew\((1-p)q\)\(b\)
LikedOld\(p(1-q)\)\(c\)
DislikedOld\((1-p)(1-q)\)\(d\)

The expected utility of a story in terms of the above probabilities and payoffs is then:

\[ u(p, q) = pq \times a + (1-p)q \times b + p(1-q) \times c + (1-p)(1-q) \times d \]

Now we just need to come up with expressions for \(p\) and \(q\). We can use the existing votes on an item to form a belief about what \(p\) might be. I’m simplifying a bit, but if we use the number of upvotes \(U\) and downvotes \(D\), we can come up with an expression for the expectation of \(p\):

\[ E[p] = \frac{U+1}{U+D+2} \]

(The +1 and +2 come in when you formulate \(p\) as a random Bayesian parameter from a beta distribution; see Bayesian Average Ratings for more on this. Anyway, it’s not that important, it’s just the percent of votes that are upvotes after you stuff an upvote and a downvote into the ballot box.)

The value of \(q\) is more interesting. Remember I said Reddit visitors reload the page at random? This is called a Poisson process with an arrival rate (reload rate) of \(\lambda\). One nice property of Poisson processes is that there’s a simple formula for describing the time between events (reloads). The probability that it has been at least \(s\) seconds since the last reload is:

\[ q = e^{-\lambda s} \]

If a story is \(t\) seconds old, then \(e^{-\lambda t}\) is the probability a random visitor hasn’t seen it before. Equipped with this amazing fact, we can write out an expectation for \(u\) in terms of upvotes \(U\), downvotes \(D\), and story age \(t\):

\[ E[u(U, D, h)] = \frac{U+1}{U+D+2} e^{-\lambda t} a + \frac{D+1}{U+D+2}e^{-\lambda t} b + \frac{U+1}{U+D+2}(1-e^{-\lambda t})c + \frac{D+1}{U+D+2}(1-e^{-\lambda t}) d \]

Now we get to the fun part. Let’s assume that we get no utility for old stories (\(c = d = 0\)), utility of \(+1\) for a new story we like (\(a = 1\)), and utility of \(-1\) for a new story we dislike (\(b = -1\)). Plugging these values in, we get:

\[ E[u(U, D, h)] = \frac{U+1}{U+D+2}e^{-\lambda t} - \frac{D+1}{U+D+2}e^{-\lambda t} \]

Or more simply:

\[ E[u] = \frac{U-D}{U+D+2}e^{-\lambda t} \]

Look familiar? No? How about if we take a logarithm, which won’t affect relative ranks:

\[ \begin{equation} \label{ln_utility} \ln E[u] = \ln{(U-D)} - \ln{(U+D+2)} - \lambda t \end{equation} \]

Now compare that to the Reddit formula:

\[ f(t_s, y, z) = y\log_{10}{z} + \frac{t_s}{45000} \]

They’re similar, and yet… different.

Anatomy of the Reddit Formula

Here’s the original statement of the Reddit formula, modified to reflect a recent change:

Given the time the entry was posted \(A\) and the time of 7:46:43 a.m. December 8, 2005 \(B\), we have \(t_s\) as their difference in seconds \[ t_s = A - B \] and \(x\) as the difference between the number of up votes \(U\) and the number of down votes \(D\) \[ x = U - D \] where \(y \in \{-1, 0, 1\}\) \[ y = \left\{ \begin{array}{lr} 1 & : x \gt 0 \\ 0 & : x = 0 \\ -1 & : x \lt 0 \end{array} \right. \] and \(z\) as the maximal value of the absolute value of \(x\) and \(1\) \[ z = \left\{ \begin{array}{lr} |x| & : |x| \ge 1 \\ 1 & : |x| \lt 1 \end{array} \right. \] we have the rating as a function \(f(t_x, y, z)\) \[ f(t_s, y, z) = y\log_{10}z + \frac{t_s}{45000} \]

There are a lot of moving parts there, so to simplify things, let’s assume \(U \gt D\). The Reddit formula then reduces down to:

\[ f(A, U, D) = \log_{10}{(U-D)} + \frac{A-B}{45000} \]

Because the rank values are relative, we can add, subtract, and divide \(f\) by positive numbers without changing the order of ranked items. So we’ll do a few rank-preserving transformations on \(f\) to see if we can beat it into formula \(\eqref{ln_utility}\). First divide by \(\log_{10}{(e)}\):

\[ f = \frac{\log_{10}{(U-D)}}{\log_{10}(e)} + \frac{A-B}{45000\log_{10}(e)} \]

If you remember your Algebra II classes (and have a TI-83 handy) you know that the above reduces to:

\[ f = \ln{(U-D)} + \frac{A-B}{19543} \]

Now we’re going to subtract an innocuous constant, the time between now (time \(NOW\)) and Reddit’s birthday \(B\), divided by the magic number 19543:

\[ f = \ln{(U-D)} + \frac{A-B}{19543} - \frac{NOW-B}{19543} \]

And reduce:

\[ f = \ln{(U-D)} + \frac{A-NOW}{19543} \]

Of course, the age of the story \(t\) is equal to \(NOW - A\), so we can rewrite this as:

\[ f = \ln{(U-D)} - \frac{t}{19543} \]

(This resolves one of the original mysteries — why the current time doesn’t appear in Reddit’s formula. It turns out you can formulate the rankings using Reddit’s birthday instead of the current time, without affecting the relative positions of stories. Rank values don’t shrink over time — instead, new stories enter the world with higher rank values than their predecessors! Born on third base, the little brats.)

Anyway, setting \(\lambda = 1/19543\), we have:

\[ f = \ln{(U-D)} - \lambda t \]

Which is remarkably close to the formula \(\eqref{ln_utility}\) we derived above. That is, whether the site designers knew it or not, the Reddit “hot” formula is close to being an optimal embodiment of utility theory under a fixed-payoff, random-reload model. Incidentally, the value of \(\lambda\) here implies that the Reddit formula is optimized for visitors who load the site every 19,543 seconds, or once every 5.43 hours. (If you load the Reddit home page more often than this, you’re probably wasting your time!)

But there are a couple of complications here: one is that the log of \((U-D)\) doesn’t exist when \(U\le D\), which is why the Reddit formula has all the weird stuff with \(y\) and \(z\). The other is that the Reddit formula, as implemented, is missing a term.

The Missing Term

Even after applying all kinds of order-preserving transformations, the Reddit formula is still missing a term compared to the formula we derived using utility theory:

\[ \ln E[u] = \ln{(U-D)} - \ln{(U+D+2)} - \lambda t \]

The missing term of course is:

\[ -\ln{(U+D+2)} \]

The term makes more sense in terms of the original (non-logarithmed) utility formula:

\[ E[u] = \frac{U-D}{U+D+2}e^{-\lambda t} \]

The above formula divides the difference in upvotes and downvotes by the total number of votes. That is, it treats upvotes and downvotes in terms of proportions — and brings highly-rated items to the top, similar to how Reddit’s comment-sorting algorithm works. Without that divisor, the Reddit formula is highly biased in favor of items that receive a lot of votes, even if a large proportion of those votes are negative.

I noted before that the Reddit formula’s \(y\) and \(z\) terms were a bit weird. They’re a workaround for the fact that \(\ln(U-D)\) doesn’t exist for \(U\le D\). The workaround has been the subject of controversy before, which I won’t rehash, but I will point out that there’s a way to render the \(y\) and \(z\) terms unnecessary. If we return to Reddit Utility Theory and set the utility of a new, disliked story to zero (that is, \(b=0\)), the expected utility becomes:

\[ E[u] = \frac{U+1}{U+D+2}e^{-\lambda t} \]

Which has a nice logarithmic form that — happily — exists for all values of \(U\) and \(D\):

\[ \ln E[u] = \ln{(U+1)} - \ln{(U+D+2)} -\lambda t \]

(The logarithmic form makes the servers happy, since the exponential form tends to drive the result to zero or to infinity quickly.)

If I may make so bold as to adapt this formula to the Reddit source code, I would write:

    cpdef double _hot(long ups, long downs, double date):
        """The hot formula. Should match the equivalent function in postgres."""
        seconds = date - 1134028003
        return round(log(ups + 1) - log(ups + downs + 2) + seconds / 19543, 7)

If you wanted to be risk-averse about promoting items with a small number of votes, you might increase the +1 and +2 terms to be larger numbers (which, in the Bayesian formulation, correspond to stronger prior beliefs about items).

Choosing Lambda

Should we keep using the same value of \(\lambda\) that Reddit currently has? I’m not sure. In addition to representing a reload rate, we can think of \(\lambda\) as a tradeoff between story age and the percent of votes that are positive. Under the new formulation, there’s a straightforward relationship between the two. Writing the fraction of positive votes as \(p\), we can calculate the substitution rate between \(p\) and \(t\) required to maintain a constant score:

\[ \frac{dE[u]/dt}{dE[u]/p} = \frac{-\lambda p e^{-\lambda t}}{e^{-\lambda t}} = -\lambda p = \frac{-\Delta p}{\Delta t} \] \[ \implies 1/\lambda = \frac{\Delta t}{\Delta p / p} \]

That is, if we decide an hour of age is equal to a five percent drop in \(p\), we would choose:

\[ 1/\lambda = \frac{3600}{.05} = 72,000 \]

Or a reload rate of once every 20 hours. Likewise, a reload rate of once every 5.43 hours implies that an hour of age is equivalent to:

\[ 19543 = \frac{3600}{\Delta p / p} \] \[ \frac{\Delta p}{p} \approx 0.18 \]

Or an 18 percent drop in the percent-positive rating. That figure may seem a bit harsh, but such is the cost of keeping fresh content up front at all times.

This final calculation highlights one of the major shortcomings of the present model: it treats stories as having a fixed payoff (\(a =\) “I like it”, \(b = \) “I don’t like it”), whereas reality is a bit messier. That is, extremely high quality links with a higher-than-normal utility payoff will tend to be swept away too quickly by this algorithm. So maybe we shouldn’t blow away the existing Reddit formula just yet.

Conclusion

I realize that proposing any change to how Reddit works is one of the Internet’s most dangerous games, so I hesitate to beat the drum in favor of MillerSort™. But I believe that expected-utility theory and a simple random-reload model can help explain why the Reddit formula has been so effective in the past, and shine a light on aspects that might be improved. In particular, the Reddit formula should probably take into account the percent of votes that are positive, rather than just taking the difference between positive and negative votes.

An even better formulation would figure out how to deal with stories of extremely high quality, and a more sophisticated model would better take into account the cost of mistakenly overranking a bad item, similar to how comment-sorting works. But the formulation above seem like a good place to start.

For the next 5.43 hours, at any rate.


You’re reading evanmiller.org, a random collection of math, tech, and musings. If you liked this you might also enjoy:


Get new articles as they’re published, via LinkedIn, Twitter, or RSS.


Want to look for statistical patterns in your MySQL, PostgreSQL, or SQLite database? My desktop statistics software Wizard can help you analyze more data in less time and communicate discoveries visually without spending days struggling with pointless command syntax. Check it out!


Wizard
Statistics the Mac way


Back to Evan Miller’s home pageSubscribe to RSSLinkedInTwitter