如何推荐下一个成就

时间:2009-07-04 08:20:50

标签: python django optimization achievements

简短版本:

我有类似StackOverflow的设置。用户获得成就。我有比SO更多的成就,让我们说10k的顺序,每个用户都有100多个成就。现在,您如何推荐(推荐)用户尝试的下一个成就?

长版:

在django中对象建模如此(仅显示重要部分):

class User(models.Model):
    alias = models.ForeignKey(Alias)

class Alias(models.Model):
    achievements = models.ManyToManyField('Achievement', through='Achiever')

class Achievement(models.Model):
    points = models.IntegerField()

class Achiever(models.Model):
    achievement = models.ForeignKey(Achievement)
    alias = models.ForeignKey(Alias)
    count = models.IntegerField(default=1)

我的算法只是找到与登录用户共享成就的所有其他用户,然后完成所有成就并按出现次数排序:

def recommended(request) :
    user = request.user.get_profile()

    // The final response
    r = {}

    // Get all the achievements the user's aliases have received 
    // in a set so they aren't double counted
    achievements = set()
    for alias in user.alias_set.select_related('achievements').all() :
        achievements.update(alias.achievements.all())

    // Find all other aliases that have gotten at least one of the same
    // same achievements as the user
    otherAliases = set()
    for ach in achievements :
        otherAliases.update(ach.alias_set.all())

    // Find other achievements the other users have gotten in addition to
    // the shared ones.
    // And count the number of times each achievement appears
    for otherAlias in otherAliases :
        for otherAch in otherAlias.achievements.all() :
            r[otherAch] = r.get(otherAch, 0) + 1

    // Remove all the achievements that the user has already gotten
    for ach in achievements :
        r.pop(ach)

    // Sort by number of times the achievements have been received
    r = sorted(r.items(), lambda x, y: cmp(x[1], y[1]), reverse=True)

    // Put in the template for showing on the screen
    template_values = {}
    template_values['achievements'] = r

但它需要FOREVER运行,并且总是返回整个列表,这是不需要的。用户只需要获得前几项成就。

所以,我欢迎提出有关其他算法和/或代码改进的建议。我会在系统中为您提供推荐算法的成就:)

2 个答案:

答案 0 :(得分:3)

您可以推荐一种方法,可以查看有多少用户已经拥有这些成就,并推荐那些受欢迎的成就。当他们取得了这些成绩后,你会从名单上下来并推荐一些不太受欢迎的名单。然而,这有一种天真的假设,即每个人都想要获得流行的成就。它可能会使流行的成就变得更受欢迎,而不那么受欢迎的成就......嗯......一个安慰是,它不会占用太多资源,而且可能会非常快速地运行。 (只需保留一份成就列表+实现的次数)

另一种方法(试图根据他已经取得的成就来猜测用户可能会追求的成就)是使用一些机器学习算法。我认为k-nearest neighbor algorithm在这里表现得相当不错。选择一个阈值,然后输出高于此阈值的所有内容。现在,我不知道这是否会比你现有的更快,但是每次用户取得新的成就时,你应该只运行一次推荐引擎,将顶部(比方说)存储为五,然后输出它无论何时需要推荐,都要回复用户。

我希望这会有所帮助。 =)

答案 1 :(得分:2)

我建议你将前三个步骤(achievement,otherAliases,count)作为一个单独的SQL语句。就像现在一样,您发出了大量查询并在Python中总结了数千行,这是您应该委派给数据库的任务。例如代码

for otherAlias in otherAliases : #For every single other user
    for otherAch in otherAlias.achievements.all() : #execute a query
        r[otherAch] = r.get(otherAch, 0) + 1

成千上万的大量查询。

相反,您可以使用SQL来实现此目的,方法是基于Alias id不同且成就ID相同而加入Achiever。然后按成就ID分组并运行计数。

在下面的查询中,表“B”是其他用户的成就,“成就者”是我们的成就。如果任何其他用户共享成就,则对于他们共享的每个成就,它们在“B”中出现一次。然后我们通过alias_id对它们进行分组并计算它们出现的次数,这样你就可以获得一个不错的id,计数表。

非常粗略的代码(这里没有SQL)

SELECT B.Alias_id, COUNT(B.achievement_id) 
  FROM Achiever, Achiever as B 
  WHERE Achiever.achievement_id == B.achievement_id 
     AND Achiever.Alias_id == <insert current user alias here>;
  GROUP BY B.Alias_id

如果按照我认为的方式工作,您将获得其他用户别名的表格,以及他们与当前用户共享的成就数量。

接下来你要做的是一个SQL语句,它使用上面的一个作为“内部选择” - 称之为用户。您可以将其与您的成就表和当前用户的成就表联系起来。除了与当前用户类似的前10位用户外,您可能希望忽略所有用户。

我现在没有时间写一个好的查询,但请查看在指定的10个用户和当前用户之间加入achievement_id的数据库的JOIN语句 - 如果没有,则将该ID设置为NULL存在。过滤器仅显示为NULL的行(未实现的成就)。