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