För programmerare: Nytt ratingsystem

Johan Rönnblom jrblom@…almers.se
Lör. 14 maj 2005 18.35.55 +0100


Hej!

Följande riktar sig endast till medlemmar med programmeringskunskaper, men
eftersom flera sådana medlemmar frågat hur min motion egentligen fungerar
så postar jag detta på listan ändå.


Det är lite svårt att få kod att se vettig ut i forumet så jag skickar den
här i stället. Det ser inte så bra ut i mail heller, men för så här pass
enkel kod så ska det väl vara läsbart. Exemplet är i ansi-C, men det bör
väl vara begripligt för folk som kan exempelvis php eller Pascal också,
tror jag.

Följande innehåller programsnuttar för att räkna ut ratingen enligt såväl
det nya som det gamla systemet. Det är alltså bara en liten del av
nedanstående som innebär någon förändring!

/* Beräkna rating enligt nya och gamla systemet.
   Indata:
    Heltalet n är antalet spelare.
    Arrayen rat[] innehåller ratingen före aktuell turnering, så att
     rat[0] innehåller den första spelarens rating, och rat[n-1]
     innehåller den sista spelarens rating.
    Heltalet m är antalet matcher.
    Arrayen matches[][] innehåller spelarnumret i varje match i, med
     vinnaren i matches[i][0] och förloraren i matches[i][1].
     Exempel: Spelare 3 vinner mot spelare 4 i match 0. Då blir
      matches[0][0] = 3
      matches[0][1] = 4
     Den sista matchen anges alltså i matches[m-1][0], matches[m-1][1]
*/

/* Sätt Y till den konstant som ratingen ska multipliceras med enligt min
  motion förslag 2b. */
int Y = 1; 
// Sätt Q till antal decimaler som ska behållas i ratingen, om så önskas
int Q = 0;

/* Denna rutin beräknar ratingförändringar för n spelare med ingångsrating
   i rat[] som spelat m matcher vilka anges i matches[][] enligt ovan.
   Notera att denna funktion är identisk i det nya och gamla systemet. Enda
   skillnaden är att den i det nya systemet måste klara av indata för
   ratingtal som inte är heltal. */

void delta_rat(float *deltas, int n, float rat[], int m, int matches[][2])
{
 int i;
 float d;

 for (i = 0; i < n; i++) // Börja med att ange förändringarna till noll
  deltas[i] = 0;

 for (i = 0; i < m; i++) // Gå igenom varje match
 {
  // Räkna ut förändringen för en viss match
  d = 10 * Y * phi((rat[matches[i][1]]-rat[matches[i][0]])/(100 * Y));
  deltas[matches[i][0]] += d; // Vinnaren får ökad rating
  deltas[matches[i][1]] -= d; // Förloraren får minskad rating
 }
 return;
}

/* Rutin som beräknar ratingen enligt det gamla systemet. */
float *oldrating(int n, float rat_before[], int m, int matches[][2])
{
 float *rat_after;
 float *deltas;
 int i;

 rat_after = (float *) malloc(n * sizeof(float)); // Minne för ny rating
 deltas = (float *) malloc(n * sizeof(float)); // Samt för förändringarna

 // Få förändringarna ur rutinen ovan
 delta_rat(deltas, n, rat_before, m, matches);

 // Den nya ratingen är den gamla plus förändringen, avrundat
 for (i = 0; i < n; i++) // Gå igenom varje spelare
  rat_after[i] = round(Q, rat_before[i] + deltas[i]);

 free(deltas);
 return rat_after;
}

/* Rutin som beräknar ratingen enligt det nya systemet. */
float *newrating(int n, float rat_before[], int m, int matches[][2])
{
 int stable = 0;
 float *rat_after;
 float *deltas;
 float rat_previous;
 int i, p;

 p = 1; // Sätt p till antalet decimaler som ska vara stabila innan avslut
 /* (Jag är beredd att satsa en slant på att det räcker med p = 0, men för
säkerhets skull kan det väl vara klokt att testa detta med den befintliga
matchdatabasen för q = 0 samt högre värden på q och se om resultaten blir
identiska.)
 */

 rat_after = (float *) malloc(n * sizeof(float)); // Minne för ny rating
 deltas = (float *) malloc(n * sizeof(float)); // Samt för förändringarna

 for(i = 0; i < n; i++)
  rat_after[i] = rat_before[i]; // Börja med ingångsratingen

 while(!stable)
 {
  // Få förändringarna ur rutinen ovan
  delta_rat(deltas, n, rat_after, m, matches);

  // Om vi inte hittar några förändringar, antag att resultatet är stabilt
  stable = 1; 
  for(i = 0; i < n; i++) // Gå igenom varje spelare
  {
   rat_previous = rat_after[i]; // Spara värdet från förra iterationen
   // Sätt den nya ratingen till den gamla plus förändringen
   rat_after[i] = rat_before[i] + deltas[i];
   // Kontrollera om någon förändring skett efter avrundning
   if (round(p, rat_after[i]) == round(p, rat_previous))
    stable = 0; // Om så är fallet har vi inte ett stabilt resultat än
  }
 }

 for(i = 0; i < n; i++) // Gå igenom varje spelare
  rat_after[i] = round(Q, rat_after[i]); // Avrunda ratingen

 free(deltas);
 return rat_after;
}

/* Rutin som avrundar talet in till d decimaler */
float round(int d, float in)
{
 int e;
 e = pow(10, d); // Sätt e till tio upphöjt till d
 return floor(in*e + 0.5)/e; // Avrunda enligt ovan
}

/* Rutinen phi måste ta flyttal som argument. En publicerad och
 dokumenterad version av phi bör användas (det finns men jag har inte
 hunnit ordna fram någon). Versionen nedan använder i stället "magiska tal"
 men kan användas av den som vill testa. */

float phi(float z)
{
 float a, b, b1, b2, b3, b4, b5, c2, n, p, t;
 int precision;

 precision = 4; /* Ange hur många decimaler av phi som ska användas. Bör
                   definieras så att funktionen garanterar korrekta värden
                   över hela intervallet. */
 if (z > 6.0) 
  return 1.0;
 if (z < -6.0)
  return 0.0;
 b1 = 0.31938153;
 b2 = -0.356563782;
 b3 = 1.781477937;
 b4 = -1.821255978;
 b5 = 1.330274427;
 p = 0.2316419;
 c2 = 0.3989423;
 a = abs(z);
 t = 1.0/(1.0+ a*p);
 b = c2*exp((-z)*(z/2.0));
 n = ((((b5*t + b4)*t + b3)*t + b2)*t + b1)*t;
 n = 1.0 - b*n;
 if (z < 0.0)
  n = 1.0 - n;
 return round(precision, n);
}

-- 
  /Johan Rönnblom




Information om listan