【问题标题】:Hot content algorithm / score with time decay热门内容算法/随时间衰减的评分
【发布时间】:2012-07-25 15:42:25
【问题描述】:

我一直在阅读 + 研究算法和公式,以便为我的用户提交的内容计算分数,以显示当前热门/热门项目,但我承认我在这里有点过头了。

我将提供一些关于我所追求的背景...用户将音频上传到我的网站,音频有几个操作:

  • 玩过
  • 已下载
  • 点赞
  • 收藏

理想情况下,我想要一种算法,每次记录新活动(播放、下载等)时我都可以更新音频分数,而且下载操作比播放更有价值,比如下载和喜欢多过喜欢。

如果可能的话,我希望超过 1 周的音频从列表中大幅下降,以使更新的内容更有可能成为趋势。

我已经阅读了看起来不错的 reddits 算法,但我对如何调整它以利用我的多个变量以及在大约 7 天后放弃旧文章感到困惑。

我们感兴趣的一些文章:

感谢任何帮助!

保罗

【问题讨论】:

  • 热门内容随时间衰减?你在说heat equation吗?不,说真的,你必须自己考虑一下——尽管热方程可以给你一些想法。
  • 嗯,问题是我在寻找什么样的方程式,我想 Zeta 回答了这个问题。我对这个级别的数学和方程式并不是很感兴趣,我希望有人以前有过这方面的经验,并找到了一些有用的博客等。

标签: algorithm sorting ranking


【解决方案1】:

Reddits 旧公式和一点下降

基本上你可以使用 Reddit 的公式。由于您的系统仅支持投票,因此您可以对它们进行加权,结果如下:

def hotness(track)
    s = track.playedCount
    s = s + 2*track.downloadCount
    s = s + 3*track.likeCount
    s = s + 4*track.favCount
    baseScore = log(max(s,1))

    timeDiff = (now - track.uploaded).toWeeks

    if(timeDiff > 1)
        x = timeDiff - 1
        baseScore = baseScore * exp(-8*x*x)

    return baseScore

exp(-8*x*x) 因素将为您提供所需的下车服务:

背后的基础

您可以使用任何比您的分数上升更快的函数归零。由于我们在分数上使用log,因此即使是线性函数也可以相乘(只要您的分数没有呈指数增长)。

所以你只需要一个函数,只要你不想修改分数,它就会返回1,然后再下降。我们上面的例子形成了这个功能:

multiplier(x) = x > 1 ? exp(-8*x*x) : 1

如果您想要不那么陡峭的曲线,您可以改变乘数。

C++ 示例

假设在给定小时内播放给定曲目的概率是 50%,下载 10%,喜欢 1%,喜欢 0.1%。然后下面的 C++ 程序会给你一个分数行为的估计:

#include <iostream>
#include <fstream>
#include <random>
#include <ctime>
#include <cmath>

struct track{
    track() : uploadTime(0),playCount(0),downCount(0),likeCount(0),faveCount(0){}
    std::time_t uploadTime;    
    unsigned int playCount;
    unsigned int downCount;
    unsigned int likeCount;
    unsigned int faveCount;    
    void addPlay(unsigned int n = 1){ playCount += n;}
    void addDown(unsigned int n = 1){ downCount += n;}
    void addLike(unsigned int n = 1){ likeCount += n;}
    void addFave(unsigned int n = 1){ faveCount += n;}
    unsigned int baseScore(){
        return  playCount +
            2 * downCount +
            3 * likeCount +
            4 * faveCount;
    }
};

int main(){
    track test;
    const unsigned int dayLength = 24 * 3600;
    const unsigned int weekLength = dayLength * 7;    

    std::mt19937 gen(std::time(0));
    std::bernoulli_distribution playProb(0.5);
    std::bernoulli_distribution downProb(0.1);
    std::bernoulli_distribution likeProb(0.01);
    std::bernoulli_distribution faveProb(0.001);

    std::ofstream fakeRecord("fakeRecord.dat");
    std::ofstream fakeRecordDecay("fakeRecordDecay.dat");
    for(unsigned int i = 0; i < weekLength * 3; i += 3600){
        test.addPlay(playProb(gen));
        test.addDown(downProb(gen));
        test.addLike(likeProb(gen));
        test.addFave(faveProb(gen));    

        double baseScore = std::log(std::max<unsigned int>(1,test.baseScore()));
        double timePoint = static_cast<double>(i)/weekLength;        

        fakeRecord << timePoint << " " << baseScore << std::endl;
        if(timePoint > 1){
            double x = timePoint - 1;
            fakeRecordDecay << timePoint << " " << (baseScore * std::exp(-8*x*x)) << std::endl;
        }
        else
            fakeRecordDecay << timePoint << " " << baseScore << std::endl;
    }
    return 0;
}

结果:

这对你来说应该足够了。

【讨论】:

  • 感谢您抽出宝贵时间清楚地解释这一点……今晚将用我的数据进行测试。
  • @Zeta 你是怎么想到 exp(-8*x*x) 的?我需要将此应用于涉及created_atupdated_at 时间戳的类似问题,其中我按updated_at 排序,但我需要它在与created_at 相差6小时后下降,我不知道如何调整公式。
  • @paulkon 回答可能有点晚了...看看 Zeta 回答中的第一张图(红色的):这是 exp(-8*x*x) 的图,显示曲目超过一周后应用于 baseScore 的下降。要在 6 小时后下车,您可以执行 timeDiff = (now - track.created_at).toHours 之类的操作,然后:if timeDiff &gt; 6 ; x = timeDiff - 6; baseScore *= exp(-8*x*x)。调整指数函数中的 8:值越高,下降越陡:) 使用 -8 :fooplot.com/plot/h0nfqukrj8 使用 -50 :fooplot.com/plot/e57bc1osnv
  • 非常好的和有帮助的答案。谢谢@Zeta!
  • @Piyush 肯定用过gnuplotplot … w l 和适当的标签。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-28
  • 1970-01-01
  • 2023-03-23
  • 2021-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多