Create a gist now

Instantly share code, notes, and snippets.

@suchowan /range.rb
Last active Oct 13, 2017

What would you like to do?
# -*- coding: utf-8 -*-
=begin
This script was written by Takashi SUGA on July-October 2017
You may use and/or modify this file according to the license described in the MIT LICENSE.txt file
https://raw.githubusercontent.com/suchowan/watson-api-client/master/LICENSE.
See also http://suchowan.at.webry.info/201707/article_30.html
=end
require 'when_exe'
require 'combination_generator'
# map メソッド
class CombinationGenerator
def to_a
combinations = []
self.each do |combination|
combinations << combination
end
combinations
end
end
# 藤井聡太 対戦相手のレートと勝敗 (2017年10月12日時点)
# レートは http://kishi.a.la9.jp/rating.html による
# 非公式戦のレートは対局日ではなく公開日のレートを用いた。
Results = %w(
1236〇 1436〇 1344〇 1340〇 1661〇 1544〇 1417〇 1555〇 1336〇 1547〇
1594〇 1527〇 1792〇 1503〇 1508〇 1442〇 1591〇 1536〇 1705〇 1773〇
1609〇 1505〇 1612〇 1585〇 1608〇 1571〇 1419〇 1762〇 1712〇 1779●
1469〇 1618〇 1701● 1279〇 1489〇 1477〇 1856● 1665〇 1383〇 1648〇
1540〇 1885● 1540● 1688〇 1651● 1489〇 1376〇 1535〇 1577〇 1657〇
1516〇 1480〇)[-30..-1]
# + %w(
# 1675〇 1807● 1554〇 1851● 1752〇 1738〇 1723〇 1785〇 1858〇 1846●
# 1618●)
# 対戦相手のレート
Opponents = Results.map(&:to_i)
# 負け数
TotalLoss = Results.select {|result| result =~ /●/ }.length
# 負け数マージン
LossMargin = 2
# 組み合わせ
laptime0 = Time.now.to_f
Combinations = (0..(TotalLoss+LossMargin)).to_a.map {|loss|
CombinationGenerator.new(loss, (0...Opponents.length).to_a).to_a
}
p ['laptime', Time.now.to_f - laptime0]
# 勝率キャッシュ
Rates = {}
# レートの種
RateSeed = 1800
# 最尤値キャッシュ
estimated = nil
# 点推定
def estimate_point
estimated ||= When::Ephemeris.root(RateSeed, 0) {|rate| change_rate(rate)}
end
# 期待負け数
def estimate_loss(rate)
(0...Opponents.length).to_a.map {|i|
game_probability(Opponents[i]-rate)
}.inject(:+)
end
# レーティング法によるレートの変化
def change_rate(rate)
estimate_loss(rate) - TotalLoss
end
# 区間推定
def estimate_range(threshold=0.05)
[[threshold,TotalLoss],[1-threshold,TotalLoss-1]].map {|th,loss|
When::Ephemeris.root(RateSeed, th) {|rate| range_probability(rate, loss)}
}
end
# 指定の負け数以下の発生確率
def range_probability(rate, loss_limit)
(0..loss_limit).to_a.map {|loss|
total_probability(rate, loss)
}.sort.inject(:+)
end
# 指定の負け数の発生確率
def total_probability(rate, loss)
Combinations[loss].map {|losses|
series_probability(rate, losses)
}.sort.inject(:+)
end
# 指定の勝敗組み合わせの発生確率
def series_probability(rate, losses)
(0...Opponents.length).to_a.map {|i|
r = rate - Opponents[i]
game_probability(losses.include?(i) ? -r : r)
}.inject(:*)
end
# 一局の勝率
def game_probability(r)
Rates[r] ||= 1 / (1 + 10.0 ** (-r / 400.0))
end
# 点推定(平衡レート)
laptime1 = Time.now.to_f
p ['---', estimate_point.round(2)] #=>
# ["---", 1856.48]
laptime2 = Time.now.to_f
p ['laptime', laptime2 - laptime1]
# 危険率 1%, 2.5%, 5% の場合の区間推定
[0.01, 0.025, 0.05].each do |threshold|
p [threshold, estimate_range(threshold).map {|rate| rate.round(2)}] #=>
# [0.01, [1660.27, 2094.04]]
# [0.025, [1686.39, 2055.38]]
# [0.05, [1709.15, 2023.46]]
end
laptime3 = Time.now.to_f
p ['laptime', laptime3 - laptime2]
# レートと負け数の関係
losses = {}
[1600,1700,1800,1900,2000,2100,2200].each do |rate|
losses[rate] = estimate_loss(rate)
end
(1..(TotalLoss+LossMargin)).each do |loss|
losses[When::Ephemeris.root(1800, loss) {|rate| estimate_loss(rate)}] = loss.to_f
end
losses.keys.sort.each do |rate|
p [rate.round(2), losses[rate].round(2)] #=>
# [1600.0, 14.43]
# [1700.0, 10.75]
# [1784.1, 8.0]
# [1800.0, 7.53]
# [1818.61, 7.0]
# [1856.48, 6.0]
# [1899.05, 5.0]
# [1900.0, 4.98]
# [1948.55, 4.0]
# [2000.0, 3.14]
# [2009.15, 3.0]
# [2090.12, 2.0]
# [2100.0, 1.9]
# [2200.0, 1.12]
# [2221.01, 1.0]
end
laptime4 = Time.now.to_f
p ['laptime', laptime4 - laptime3]
# 最尤値での負け数ごとの確率
p (0..TotalLoss+LossMargin).to_a.map {|loss|
total_probability(estimate_point, loss).round(4) #=>
# [0.0008, 0.0071, 0.0288, 0.0734, 0.132, 0.1785, 0.1889, 0.1608, 0.1124]
}
p ['laptime', Time.now.to_f - laptime4]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment