From 73bc8ffa23808715efc94034319e4a8fb2923cb7 Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Tue, 30 Jul 2013 02:12:55 -0700 Subject: [PATCH] add comments, change constant name, add ProbWin() --- go/glicko2.go | 53 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/go/glicko2.go b/go/glicko2.go index 9285079..3ac9291 100644 --- a/go/glicko2.go +++ b/go/glicko2.go @@ -16,7 +16,8 @@ import ( "math" ) -const dvolatility float64 = 0.3 +// system constant, determines delta volatility over time; should be [0.3,1.2] +const kDVOL float64 = 0.3 // Glicko2 state reprensentation (Glicko2 uses different scales from Glicko) type Glicko2 struct { @@ -47,6 +48,11 @@ func newGlicko2(rating float64, deviation float64, volatility float64) *Glicko2 return g } +// get the probability of beating opponent +func (g *Glicko2) ProbWin(opponent *Glicko2) float64 { + return 1.0 / (1.0 + math.Pow(10.0, ((opponent.Rating()-g.Rating())/(400.0*math.Sqrt(1.0+0.0000100723986*((g.Deviation()*g.Deviation())+(opponent.Deviation()*opponent.Deviation()))))))) +} + // Get the current rating. This rating is a Glicko rating, not a Glicko2 rating. // For details, please see http://www.glicko.com/ func (g *Glicko2) Rating() float64 { @@ -127,12 +133,12 @@ func (g *Glicko2) Print() { } } -// util func +// util func 'g' func calcG(deviation float64) float64 { return 1.0 / (math.Sqrt(1.0 + 3.0*(deviation*deviation)/(math.Pi*math.Pi))) } -// util func +// util func 'E' func calcE(rating float64, rating_opponent float64, deviation_opponent float64) float64 { return 1.0 / (1.0 + math.Exp(-calcG(deviation_opponent)*(rating-rating_opponent))) } @@ -143,13 +149,26 @@ func (g *Glicko2) Update() { // merge wins, losses, draws slices for convenience results := append(append(g.wins, g.losses...), g.draws...) - // bail if no opponents set + // Note that if a player does not compete during the rating period, then + // only Step 6 applies. if len(results) == 0 { g.deviation = math.Sqrt((g.deviation * g.deviation) + (g.volatility * g.volatility)) return } - // compute variance + // Step 1. Determine a rating and RD for each player at the onset of the + // rating period. The system constant which constrains the change in + // volatility over time, needs to be set prior to application of the system. + // Reasonable choices are between 0.3 and 1.2, though the system should be + // tested to decide which value results in greatest predictive accuracy + // ... (ratings already stored in instance) + + // Step 2. For each player, convert the ratings and RD's onto the Glicko-2 + // scale. + // ... (ratings already stored in G2 format) + + // Step 3. Compute the quantity v. This is the estimated variance of the + // team's/player's rating based only on game outcomes. variance := 0.0 for _, r := range results { g_i := calcG(r.deviation) @@ -158,7 +177,9 @@ func (g *Glicko2) Update() { } variance = 1.0 / variance - // compute delta + // Step 4. Compute the quantity 'delta', the estimated improvement in rating + // by comparing the pre-period rating to the performance rating based only + // on game outcomes. delta := 0.0 for _, r := range g.wins { delta += calcG(r.deviation) * (1.0 - calcE(g.rating, r.rating, r.deviation)) @@ -171,7 +192,7 @@ func (g *Glicko2) Update() { } delta *= variance - // determine new volatility + // Step 5. Determine the new value of the volatility. new_volatility := 0.0 a := math.Log((g.volatility * g.volatility)) x := 0.0 @@ -179,17 +200,17 @@ func (g *Glicko2) Update() { for math.Abs(x-x_new) > 0.0000001 { x = x_new d := (g.deviation * g.deviation) + variance + math.Exp(x) - h1 := -(x-a)/(dvolatility*dvolatility) - 0.5*math.Exp(x)/d + 0.5*math.Exp(x)*(delta/d)*(delta/d) - h2 := -1.0/(dvolatility*dvolatility) - 0.5*math.Exp(x)*((g.deviation*g.deviation)+variance)/(d*d) + 0.5*(delta*delta)*math.Exp(x)*((g.deviation*g.deviation)+variance-math.Exp(x))/(d*d*d) + h1 := -(x-a)/(kDVOL*kDVOL) - 0.5*math.Exp(x)/d + 0.5*math.Exp(x)*(delta/d)*(delta/d) + h2 := -1.0/(kDVOL*kDVOL) - 0.5*math.Exp(x)*((g.deviation*g.deviation)+variance)/(d*d) + 0.5*(delta*delta)*math.Exp(x)*((g.deviation*g.deviation)+variance-math.Exp(x))/(d*d*d) x_new = x - h1/h2 } new_volatility = math.Exp(x_new / 2.0) - // update the rating deviation to the new pre-rating period value + // Step 6. Update the rating deviation to the new pre-rating period value. pre_deviation := math.Sqrt((g.deviation * g.deviation) + (new_volatility * new_volatility)) - // update the rating and deviation - new_deviation := 1.0 / (math.Sqrt(1.0/(pre_deviation * pre_deviation) + 1.0/variance)) + // Step 7. Update the rating and RD to the new values. + new_deviation := 1.0 / (math.Sqrt(1.0/(pre_deviation*pre_deviation) + 1.0/variance)) new_rating := 0.0 for _, r := range g.wins { new_rating += calcG(r.deviation) * (1.0 - calcE(g.rating, r.rating, r.deviation)) @@ -203,11 +224,11 @@ func (g *Glicko2) Update() { new_rating = new_rating * (new_deviation * new_deviation) new_rating += g.rating - // wipe our result lists - g.ClearResults() - - // copy new values + // Step 8. Convert ratings and RD's back to original scale. g.deviation = new_deviation g.volatility = new_volatility g.rating = new_rating + + // wipe our result lists + g.ClearResults() }