2009-09-08 19 views
8

Disons que j'ai environ 1 000 000 utilisateurs. Je veux savoir quelle est la position d'un utilisateur et quels utilisateurs l'entourent. Un utilisateur peut obtenir une nouvelle réalisation à tout moment, et s'il pouvait voir sa mise à jour permanente, ce serait merveilleux. Honnêtement, chaque fois que je pense à faire cela serait horriblement cher dans le temps et/ou de la mémoire. Des idées? Mon idée la plus proche jusqu'ici est de commander les utilisateurs hors ligne et de construire des compartiments de centiles, mais cela ne peut pas montrer à un utilisateur sa position exacte.Django: Comment créer un leaderboard

Une partie du code si cela vous aide django personnes:

class Alias(models.Model) : 
    awards = models.ManyToManyField('Award', through='Achiever') 

    @property 
    def points(self) : 
     p = cache.get('alias_points_' + str(self.id)) 
     if p is not None : return p 

     points = 0 
     for a in self.achiever_set.all() : 
      points += a.award.points * a.count 

     cache.set('alias_points_' + str(self.id), points, 60 * 60) # 1 hour 
     return points 

class Award(MyBaseModel): 
    owner_points = models.IntegerField(help_text="A non-normalized point value. Very subjective but try to be consistent. Should be proporional. 2x points = 2x effort (or skill)") 
    true_points = models.FloatField(help_text="The true value of this award. Recalculated with a cron job. Based on number of people who won it", editable=False, null=True) 

    @property 
    def points(self) : 
     if self.true_points : 
      # blend true_points into real points over 30 days 
      age = datetime.now() - self.created 
      blend_days = 30 
      if age > timedelta(days=blend_days) : 
       age = timedelta(days=blend_days) 
      num_days = 1.0 * age.days/blend_days 
      r = self.true_points * num_days + self.owner_points * (1 - num_days) 
      return int(r * 10)/10.0 

     else : 
      return self.owner_points 


class Achiever(MyBaseModel): 
    award = models.ForeignKey(Award) 
    alias = models.ForeignKey(Alias) 
    count = models.IntegerField(default=1) 

Répondre

4

Je pense que Counterstrike résout ce problème en obligeant les utilisateurs à atteindre un seuil minimum pour devenir classé - il vous suffit de trier avec précision les 10% ou tout .

Si vous souhaitez trier tout le monde, sachez que vous n'avez pas besoin de les trier parfaitement: triez-les en 2 chiffres significatifs. Avec les utilisateurs de 1M, vous pouvez mettre à jour le classement pour les 100 meilleurs utilisateurs en temps réel, les 1000 utilisateurs suivants pour les 10 utilisateurs les plus proches, puis les masses pour les 1% ou 10% les plus proches. Vous ne passerez pas de la place 500 000 à la place 99 en un tour.

Il ne sert à rien de placer le contexte de 10 utilisateurs au-dessus et au-dessous de 500 000 - l'ordre des masses sera incroyablement nerveux d'un tour à l'autre en raison de la distribution exponentielle.

Éditer: Jetez un oeil à la SO leaderboard. Maintenant, allez à page 500 sur 2500 (environ 20e centile). Y a-t-il quelque chose à dire aux gens avec le représentant '157' que les 10 personnes de chaque côté d'eux ont aussi le représentant '157'? Vous sautez 20 endroits de toute façon si votre représentant monte ou descend un point. Plus extrême, est-ce que maintenant les 1056 pages inférieures (sur 2538), ou les 42% inférieurs des utilisateurs, sont à égalité avec rep 1. vous obtenez un point de plus, et vous avez sauté 1055 pages. Ce qui représente une augmentation d'environ 37 000 rangs. Il pourrait être cool de leur dire "vous pouvez battre 37k personnes si vous obtenez un point de plus!" mais est-ce important combien de chiffres significatifs le nombre 37k a?

Il n'y a aucune valeur à connaître vos pairs sur un ladder jusqu'à ce que vous soyez déjà au sommet, parce que n'importe où mais le sommet, il y a un nombre écrasant d'entre eux.

+0

quelqu'un s'il vous plaît modifier cela pour être plus articulé, je vais au lit. –

+0

J'essayais de donner un but aux utilisateurs en leur montrant des personnes au-dessus d'eux pour les battre, mais pas trop loin pour être inaccessible. –

+0

la gigue vers le bas de la distribution sera si grande que même en montant ou descendant 1 point vous laissera tomber ou vous gagnerez plusieurs milliers de places sur 1M. vous devriez mesurer à quoi ressemble votre distribution de score. –

0

Un million n'est pas tellement, je l'essaierais d'abord facilement. Si la propriété points est la chose sur laquelle vous triez, elle doit être une colonne de base de données. Alors vous pouvez juste faire un compte de points plus grand que la personne en question pour obtenir le rang. Pour obtenir d'autres personnes à proximité d'une personne en question, vous faites une requête auprès des personnes ayant des points plus élevés et le tri ascendant le limite au nombre de personnes que vous voulez.

La chose délicate sera de calculer les points sur enregistrer. Vous devez utiliser l'heure actuelle comme multiplicateur de bonus. Un point doit maintenant se transformer en un nombre inférieur à 1 point dans 5 jours. Si vos utilisateurs gagnent fréquemment des points, vous devrez créer une file d'attente pour gérer le chargement.