なんちゃってレーティング計算

例えば、A〜Dの4人の勝率マトリクスが以下のようになっていたときに、

A B C D
AのB,C,Dへの勝率 0.50 0.10 0.90 0.99
B 0.90 0.50 0.80 0.99
C 0.10 0.20 0.50 0.99
D 0.01 0.01 0.01 0.50

このようなレーティングを計算するコードです(A=2150で固定とします)

A=2150
B=2429
C=1872
D=1204

レーティングの理論は本当は難しいのだろうけど、
次のように簡単な最小二乗法で求めてみました。
間違っていたらすいません。
まず、目的関数を勝率y[i][j]とレーティング関数p(x)との二乗差で定義します。
D=sum_ij(y[i][j] - p(x[i] - x[j]))^2
レーティング関数p(x)はx=150のときに0.7になるようなシグモイド関数を選びました。
次に、初期値x[i]を適当に設定して、上式の勾配、
∂D/∂x[i] = sum_j(y[i][j] - p(x[i] - x[j]))*p'(x[i] - x[j])
で更新します。これをなんどか繰り返せば、そのうち収束します。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>


// 勝率7割がR=150点差となるようなシグモイド関数p(x)
double sig(double x)
{
	return (exp(x/150.0)) / (1.0 + exp(x/150.0));
}
double dsig(double x)
{
	double a = (1.0 + exp(x/150.0));
	return (exp(x/150.0)) / (a*a);
}

//
//  A,B,C,Dの4人のrartingを最急降下により求める。
//  ただし、Aのratingは2150に固定。
//
int main()
{
	// 勝率マトリクス
	double y[16][16] =
	{
           //      A     B     C     D
		{0.50, 0.10, 0.90, 0.99}, // A のB, C, Dへの勝率
		{0.90, 0.50, 0.80, 0.99},
		{0.10, 0.20, 0.50, 0.99},
		{0.01, 0.01, 0.01, 0.50}
	};
	double x[16], dx[16];
	int i, j, N, it;
	N = 4; // 人数

	// 初期化
	for(i = 0;i < N;i++)
		x[i] = 2150;
	
	// 適当に反復計算
	for(it = 0;it < 10000;it++)
	{
		double err = 0.0;
		double max_dx = -100000;
		printf("x = [%d ", (int)x[0]);
		// 微分値初期化
		for(i = 0;i < N;i++)
		{
			dx[i] = 0.0;
		}
		// 微分値を求める
		for(i = 1;i < N;i++)
		{
			for(j = 0;j < N;j++)
			{
				double d = x[i] - x[j];
				double dv = (y[i][j] - sig(d));
				dx[i] = dx[i] + dv * dsig(d);
				// 誤差もついでに計算
				err += dv * dv;
			}
			// 微分値の最大値を保存
			// 最大の更新量が1となるようにするため
			if(max_dx < fabs(dx[i]))
				max_dx = fabs(dx[i]);
		}
		// 更新実行
		for(i = 1;i < N;i++)
		{
			x[i] = x[i] + 1.0 * dx[i] / max_dx;
			printf("%d ", (int)x[i]);
		}
		printf("] err = %.4f\n", err);
	}	
}