0

My question is related to this one: Convert a number range to another range, maintaining ratio.

I have a model, Contest, which contains many Teams, which accrue points by doing activities over time. The teams are of different size, and I am calculating a handicap factor for each one, in order to adjust the points relative to the team size:

   Team.rb: 

   def handicap_factor
      return (contest.total_users / contest.teams.count.to_f) / self.total_users
   end

This returns a decimal value which I multiply against the team's total points to get a weighted points score:

    Team.rb:

    def adjusted_points
      return (points * handicap_factor).ceil
    end

Here are some sample handicap factors:

 team1.handicap_factor: 1.73125
 team2.handicap_factor: 0.6925
 team3.handicap_factor: 1.08203125
 team4.handicap_factor: 0.721354

Now, I want to convert the range of the handicap factors so that the lowest value is 1 (so that even for the team with the lowest handicap factor, an action results in at least one point), while maintaining the ratio of difference between them.

I am unsure what the correct architecture of this would be between the Contest and the Team models. I guess that I could add a method on Contest.rb to return the multiplication factor required to shift the lowest team.handicap_factor to 1, and then add a method to Team.rb to multiply team.handicap_factor by this.

Is that a reasonable way to approach this problem? Or, is there a more efficient way of handling this?

Community
  • 1
  • 1
Ali H
  • 779
  • 2
  • 9
  • 21
  • Adding methods is definitely the appropriate path to take. I'm not sure exactly what the rules are for your normalisation though. What is the range of actual values (ie lowest and highest possible values), and what is the desired range of values? – Max Williams Feb 17 '15 at 12:50
  • 1
    Oh sorry, there's no maximum values are there...you just want the ratio to stay the same. Answer coming up ... – Max Williams Feb 17 '15 at 12:51
  • Yes, there is no maximum value to the range, only a minimum value of 1. – Ali H Feb 17 '15 at 13:04
  • It just occurred to me that your schema may not be right: do you have, in Team, `belongs_to :contest`? This is implied by your saying `contest.total_users` in the handicap_factor method. This would mean that each team can only ever be in a single contest, which doesn't sound right. Surely a given team can enter lots of contests? If that was the case, would they have a different handicap factor for each contest? – Max Williams Feb 17 '15 at 14:19

1 Answers1

0

I think you've got an underlying mathematical problem here. Let's work through it step by step.

Lets say we start with

team_a.handicap_factor = 0.5
team_b.handicap_factor = 1

Now, team_b's handicap is twice that of team_a, and we want the minimum to be 1. So, team_a's adjusted_handicap should be 1, and team_b's adjusted_handicap needs to be increased to 2, in order to be twice that of team_a.

So far so good. Right?

Then, team_c comes along who have an unadjusted handicap of 0.4. The unadjusted ratio compared to team_a is 0.4/0.5 = 0.8, and we still want our minimum of 1. So, we set team_c's adjusted handicap to 1, which means we need to raise the adjusted handicap of everyone else in order to keep the ratio compared to team c. In other words, there's a new scaling factor to get from handicap_factor to adjusted_handicap, and everyone else gets this scaling factor applied to their score. The new scaling factor here is 1/0.4 = 2.5. So, now we have this situation:

Scaling factor = 1/<lowest unadjusted handicap> = 1/0.4 = 2.5

team           handicap_factor          adjusted handicap
team_a         0.5                      1.25
team_b         1                        2.5
team_c         0.4                      1

So, every time a new lowest handicap comes along, everyone else's adjusted handicap will go up. Is this definitely what you want?

If it is what you want, then you're going to want to start storing handicap_factor in the database, in order for you to quickly get the lowest handicap_factor for any team, without having to load all teams and calculate it to see which is the lowest.

Max Williams
  • 32,435
  • 31
  • 130
  • 197