詳細な説明は、以下の解説記事をご覧ください。
javascriptにより、ブラウザから実行できるプログラムです。単一のhtmlファイルですので、何をしているのかはソースを見れば分かります。
- http://lfics81.techblog.jp/confidence_interval_ja.html(日本語版)
- http://lfics81.techblog.jp/confidence_interval.html(英語版)
一応、安全性は確認しておりますが、バグやファイル改ざん等のリスクまでは対応できません。利用する際には自己責任でお願い致します。このプログラムの使用に関して、当方は一切の責任を負いません。
古いブラウザだと動かないかもしれません。
対局数が多い場合には、処理に時間がかかることがあります。
英語版の表記は以下の意味です。
- 「games」:対局数。
- 「wins」:勝ち数。
- 「draws」:引き分け数。
- 「confidence interval」:信頼区間。
- 「Calculate」:計算実行。
- 「Select all」:全選択。
- 「winning percentage」:勝率。
- 「standard error」:標準誤差。
- 「relative Elo rating」:相対イロレーティング。
「removed from games」は引き分けを対局数から取り除く場合、「transformed into (draws / 2) wins」は引き分け数の半分(端数は切り捨て)を勝ち数に加える場合に選択してください。
このプログラムは、パブリックドメイン(公有)扱いとします。ご自由にお使いください。
以下はc++版の関数です。やっていることは基本的にjavascript版と同じですが、コメントが付いています。
#include <algorithm> // std::minとstd::maxのため。windows.hの名前衝突に注意。
#include <cmath> // 数学関数のため。
// ----------------------------------------------
double log10_binomial(const int & n, const int & k)
// 二項係数 C(n, k) の常用対数。
// ----------------------------------------------
{
const int m = std::min(k , (n - k));
double s = 0.0;
for (int i = 1; i <= m; i++)
{
s += std::log10((n - m + i) / double(i));
// オーバーフロー対策で対数での計算。
// 常識的な範囲の計算なら対数を取らずに掛け算のやり方でもよい。
// javascript版では場合分けで少し高速化。
}
return(s);
}
// ----------------------------------------------
double binomial_distribution(const int & n, const int & k, const double & p)
// 二項分布 C(n, k) * p^{k} * (1 - p)^{n - k} の値。
// ----------------------------------------------
{
return(std::pow(10.0, (log10_binomial(n, k) + k * std::log10(p) + (n - k) * std::log10(1.0 - p))));
// オーバーフロー対策で対数での計算。
}
// ----------------------------------------------
void confidence_interval(const int & n, const int & k, const double & a, double & p, double & p_min, double & p_max, double & dr, double & dr_min, double & dr_max)
// 入力:対局数n、勝利数k、[100 * (1 - a)]%信頼区間のa。
// 出力:勝率推定値p、勝率下限値p_min、勝率上限値p_max、レート差推定値dr、レート差下限値dr_min、レート差上限値dr_max。
// ----------------------------------------------
{
if ((n < 5) || (k <= 0) || (k >= n) || (a <= 0.0) || (a >= 1.0))
{
// 本来はエラー処理、ここでは省略。
return;
}
// 勝率推定。
p = k / double(n);
// 挟み撃ちで区間を絞っていくための初期値。
int k_min = 0;
int k_max = n;
// 区間境界での二項分布の値の初期値。
double b_min = binomial_distribution(n, k_min, p);
double b_max = binomial_distribution(n, k_max, p);
// 確率の積算値。0から始めて、a越えを目指す。
double s = 0.0;
while (true) // a超えまでの無限ループ。
{
// 境界の分布値が小さい方を狭めていく。
if (b_min < b_max)
{
s += b_min;
if (s >= a) {break;} // 超えたら抜ける。越える前が信頼区間。
k_min++;
b_min = binomial_distribution(n, k_min, p);
}
else
{
s += b_max;
if (s >= a) {break;} // 超えたら抜ける。越える前が信頼区間。
k_max--;
b_max = binomial_distribution(n, k_max, p);
}
}
// 区間を勝率に直す。
p_min = k_min / double(n);
p_max = k_max / double(n);
// レート差の計算。
dr = 400.0 * std::log10(p / (1 - p));
dr_min = (k_min == 0) ? -1.E308 : (400.0 * std::log10(p_min / (1 - p_min)));
dr_max = (k_max == n) ? 1.E308 : (400.0 * std::log10(p_max / (1 - p_max)));
// 念のために対数の発散を避けているが、普通の環境なら不要。
return;
}

コメント
コメント一覧 (2)
http://tadaoyamaoka.hatenablog.com/entry/2017/06/14/203529
どちらが正しいのでしょうか?
調べた限りでは統計的では
上記のブログの式を使うのが一般的なようですが
このブログも説得力があるのでどちらが正しいのか判断に迷っております
コメントありがとうございます。
ご指摘の記事の内容は本記事の内容と整合しており、どちらにも間違いはないと思われます。
両記事の相違は問題設定の違いにあります。本記事では「勝率/レートの精度の評価方法」を問題にしており、件の記事では「強くなったかどうかの判定方法」を問題にしています。問題設定が違うだけで、両者の手法は基本的に同じです。
実際に、条件を揃えると、計算結果も一致します。ただし、「勝率の推定」の信頼区間は大きすぎても小さすぎてもダメなので“両側”になるのに対して、「強くなったかどうかの判定」では勝率が5割より小さい場合のみがダメなので“片側”になるという点に注意してください。
具体的に、件の記事の最後の方にある計算例で試してみましょう。100局で58勝42敗、“片側”5%は“両側”10%なので90%信頼区間で信頼区間計算機にかけると、勝率の信頼区間の下限が0.5となり、信頼区間内の全てが勝率5割以上となって「強くなった」という判定になります。
細かな違いとしては、件の記事では正規分布を用いているのに対して、この計算機ではより詳細な二項分布を用いています。両者は対局数が十分に大きければ一致します(中心極限定理)。