在给定完整历史的情况下计算团队赢得体育比赛的几率的算法

时间:2014-11-06 00:03:32

标签: algorithm math statistics game-theory

假设:

  • 团队永远不会改变
  • 团队技能不提高
  • 每个团队与其他团队的某些子集的表现的完整历史已知
  • 团队之间的比赛数量很大,但可能很少(每支队伍都没有互相比赛)

例如:

我有一长串匹配结果,如下所示:

Team A beats Team B
Team B beats Team A
Team A beats Team B
Team C beats Team A
Team A beats Team C

问题:

预测任何球队击败任何其他球队的正确投注赔率。

在上面的例子中,我们可能得出结论,A应该在66%的时间内击败B.这是基于直接观察,非常简单。然而,找到C击败B的概率似乎更难。他们从未在一起玩过,但似乎很可能是C> B,信心不足。

研究我已完成:

我已经阅读了很多关于技能游戏的不同排名系统,例如Chess的Elo和Glicko评级系统。这些因素不足,因为他们对所涉及的概率分布做出了假设。例如,Elo的核心假设是每个游戏中每个玩家的国际象棋表现是一个正态分布的随机变量。但是,根据维基百科,还有其他发行版更适合现有数据。

我不想承担分发。在我看来,手头有超过10,000个匹配结果,我应该能够从证据中推断出分布(我不知道如何做到这一点),或者使用某种强化学习方案,而不是# 39;关心分布是什么。

7 个答案:

答案 0 :(得分:5)

您希望对概率(或多个概率)进行最佳估计,并在更多数据可用时不断更新估算值。这需要Bayesian inference!贝叶斯推理是基于这样的观察,即A和B两种情况(同时)的概率(分布)等于A的概率(分布),因为B是案例乘以概率的情况B就是这样。在公式中:

  

P(A,B)= P(A | B)P(B)

以及

  

P(A,B)= P(B | A)P(A)

因此

  

P(A | B)P(B)= P(B | A)P(A)

将P(B)带到另一边,我们得到贝叶斯更新规则

  

P(A | B)' = P(B | A)P(A)/ P(B)

通常A代表你想要估算的任何变量(例如"团队x击败团队y")而B代表你的观察(例如团队之间赢得和失败的完整历史记录)。我写了素数(即 P(A | B)' 中的引号),表示等式的左手代表了你的信念的更新。为了使其具体化,你的新的估计团队x将击败团队y的概率,给出所有观察到目前为止,是给出这些观察的概率给出您先前的估算值,乘以之前的估算值除以您看到的观察结果的总体概率(即没有假设团队之间的相对强度;一个团队赢得大部分时间比两队经常获胜的可能性更小。)

P(A | B)'从当前更新的左侧变为下一次更新右侧的新P(A)。您只需继续重复此操作即可获得更多数据。通常,为了尽可能无偏见,您可以从P(A)的完全平坦分布开始。随着时间的推移,P(A)将变得越来越确定,尽管该算法能够很好地处理您尝试估计的潜在概率的突然变化(例如,如果团队x突然变得更强,因为新的球员加入球队。)

好消息是,贝叶斯推理与ElKamina也提到的beta distribution一起运作良好。实际上,这两者通常组合在用于学习概率分布的人工智能系统中。虽然beta版本本身仍然是一个假设,但它的优势在于它可以采用多种形式(包括完全平坦且非常灵活),因此相对没有理由担心您的分发选择可能会影响你的结果。

一个坏消息是除了beta分布之外,你还需要做出假设。例如,假设您有以下变量:

  

A:团队x击败团队y

     

B:团队y击败团队z

     

C:团队x击败团队z

你可以从x和y之间的直接匹配以及y和z之间的匹配得到观察,但是不能从x和z之间的匹配得到观察。估计P(C)的一种简单(虽然简单)的方法可能是假设传递性:

  

P(C)= P(A)P(B)

无论您的方法多么复杂,您都必须定义某种概率结构来处理数据中的差距和相互依赖性。无论你选择什么样的结构,它都将是一个假设。

另一个坏消息是,这种方法很复杂,我无法完整地介绍如何将其应用到您的问题中。鉴于你需要一个相互依赖概率的结构(团队x给团队y的概率给出涉及团队x,y和z的其他分布),你可能想要使用Bayesian network或相关分析(例如{{3} }}或Markov random field)。

我希望这会有所帮助。无论如何,请随时要求澄清。

答案 1 :(得分:2)

我们需要做出一些假设,如下例所示:

Team Rock beats Team Scissors
Team Paper beats Team Rock
Team Rock beats Team Scissors

现在我们在Team Scissors和Team Paper之间展开了一场战斗。自从Team Paper击败Team Rock再次击败Scissors两次之后,我们可能会认为最好的赔率是Paper beating Scissors。

然而,在上面我们假设了一个传递模型,显然不适用。它可能更适合像足球这样的运动,但仍然不完全适合。

ELO所做的是假设所有团队都具有从0infinity的某种“固有力量”。很明显,没有技能真的像这样工作,但事实证明它是预测游戏的有用模型。还要注意这个模型如何不适用于Rock,Paper,Scissors游戏。

国际象棋中的下一个假设是“内在力量”的绝对差异会产生一个玩家击败另一个玩家的可能性的概率分布。再次,这显然不是真的,因为白色/黑色片段也会发挥作用。然而,通过观察多场比赛的获胜机会,可以使其更精确(显示证据)。

通过以上两个假设,我们可以计算获胜机会,如果模型最适合游戏,它们可能相当精确。不幸的是,这种建模并不是一门精确的科学,无论你做出什么样的假设,你总能找到一个他们不适用的游戏/情境。

我希望这给你一些灵感,为你的模型提出更多的假设:)一旦你拥有它们,你可以通过查看它是否可以预测你在培训中已经拥有的一些结果来测试它的工作情况组。有一整个机器学习和统计学文献供您欣赏:)

答案 2 :(得分:2)

评级百分比指数 - 使用以下C#实现:

// <copyright file="RPICalculator.cs" company="Steve Stokes Consulting, LLC">
//  Copyright © Steve Stokes Consulting, LLC. All Rights Reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RPI.Calculator
{
    public class RPICalculator
    {
        public void Test()
        {
            //Home      Score   Away        Score
            //UConn     64      Kansas      57
            //UConn     82      Duke        68
            //Minnesota 71      UConn       72
            //Kansas    69      UConn       62
            //Duke      81      Minnesota   70
            //Minnesota 52      Kansas      62

            //resulting in the following ratings:
            //UConn: 0.6660
            //Kansas: 0.6378
            //Duke: 0.3840
            //Minnesota: 0.3403

            List<Team> Teams = new List<Team>();
            List<Match> Matches = new List<Match>();

            Teams.Add(new Team()
            {
                Name = "UConn",
            });
            Teams.Add(new Team()
            {
                Name = "Kansas",
            });
            Teams.Add(new Team()
            {
                Name = "Duke",
            });
            Teams.Add(new Team()
            {
                Name = "Minnesota",
            });

            Matches.Add(new Match()
            {
                HomeTeam = Teams.Where(t => t.Name == "UConn").First(),
                AwayTeam = Teams.Where(t => t.Name == "Kansas").First(),
                Winner = Teams.Where(t => t.Name == "UConn").First(),
            });
            Matches.Add(new Match()
            {
                HomeTeam = Teams.Where(t => t.Name == "UConn").First(),
                AwayTeam = Teams.Where(t => t.Name == "Duke").First(),
                Winner = Teams.Where(t => t.Name == "UConn").First(),
            });
            Matches.Add(new Match()
            {
                HomeTeam = Teams.Where(t => t.Name == "Minnesota").First(),
                AwayTeam = Teams.Where(t => t.Name == "UConn").First(),
                Winner = Teams.Where(t => t.Name == "UConn").First(),
            });
            Matches.Add(new Match()
            {
                HomeTeam = Teams.Where(t => t.Name == "Kansas").First(),
                AwayTeam = Teams.Where(t => t.Name == "UConn").First(),
                Winner = Teams.Where(t => t.Name == "Kansas").First(),
            });
            Matches.Add(new Match()
            {
                HomeTeam = Teams.Where(t => t.Name == "Duke").First(),
                AwayTeam = Teams.Where(t => t.Name == "Minnesota").First(),
                Winner = Teams.Where(t => t.Name == "Duke").First(),
            });
            Matches.Add(new Match()
            {
                HomeTeam = Teams.Where(t => t.Name == "Minnesota").First(),
                AwayTeam = Teams.Where(t => t.Name == "Kansas").First(),
                Winner = Teams.Where(t => t.Name == "Kansas").First(),
            });

            var results = Calculate(Teams, Matches, Sport.NCAA_Basketball);

            foreach (var team in results)
            {
                Debug.WriteLine(string.Format("{0} - {1}", team.Name.PadRight(Teams.Select(t => t.Name).Max(s => s.Length)), team.RPI));
            }
        }

        private decimal defaultHomeWinValue = 1.0m;
        private decimal defaultAwayWinValue = 1.0m;
        private decimal homeWinValue = 1.0m;
        private decimal awayWinValue = 1.0m;

        public IEnumerable<TTeam> Calculate<TTeam, TMatch>(IEnumerable<TTeam> Teams, IEnumerable<TMatch> Matches, Sport Sport)
        {
            // TODO: transform a user team to our internal team type
        }

        /// <summary>
        /// Calculate the RPI of each team
        /// </summary>
        /// <param name="Teams">The list of teams to calculate RPI for</param>
        /// <param name="Matches">The list of matches and results of the matches the teams played in a period</param>
        /// <param name="Sport">The sport the teams played - this modifies home/away weighting based on NCAA rules</param>
        /// <returns>The list of teams with calculated RPI's</returns>
        public IEnumerable<Team> Calculate(IEnumerable<Team> Teams, IEnumerable<Match> Matches, Sport Sport)
        {
            SetWeightingBasedOnSport(Sport);

            foreach (var team in Teams)
            {
                // calculate the WP of each team
                team.WP = CalculateWinPercent(team, Matches, homeWinValue, awayWinValue);

                // calculate the OWP of each team
                team.OWP = CalculateOpponentsWinPercent(team, Matches);

                // calculate the OOWP of each team
                team.OOWP = CalculateOpponentsOpponentsWinPercent(team, Teams, Matches);

                // calculate the RPI for each team
                team.RPI = CalculateRPI(team);
            }

            return Teams.OrderByDescending(t => t.RPI);
        }

        private decimal CalculateRPI(Team team)
        {
            //RPI = (WP * 0.25) + (OWP * 0.50) + (OOWP * 0.25)

            //UConn: 0.6660
            //Kansas: 0.6378
            //Duke: 0.3840
            //Minnesota: 0.3403

            var RPI = (team.WP * 0.25m) + (team.OWP * 0.50m) + (team.OOWP * 0.25m);

            return Math.Round(RPI, 4);
        }

        private decimal CalculateOpponentsOpponentsWinPercent(Team teamInQuestion, IEnumerable<Team> Teams, IEnumerable<Match> Matches)
        {
            //UConn: ((Kansas 0.6667) + (Kansas 0.6667) + (Duke 0.3333) + (Minnesota 0.3889)) / (4 games) = 0.5139
            //Kansas: ((UConn 0.7500) + (UConn 0.7500) + (Minnesota 0.3889)) / (3 games) = 0.6296
            //Duke: ((UConn 0.7500) + (Minnesota 0.3889)) / (2 games) = 0.5694
            //Minnesota: ((UConn 0.7500) + (Duke 0.3333) + (Kansas 0.6667)) / (3 games) = 0.5833

            // get each team i've played this season (not unique)
            var teamsIvePlayed = Matches.Where(m => m.AwayTeam == teamInQuestion || m.HomeTeam == teamInQuestion).Select(s => s.HomeTeam == teamInQuestion ? s.AwayTeam : s.HomeTeam);

            // get the opponent win percent (OWP) of each team I played
            var teamsIvePlayedOpponentWinPercent = teamsIvePlayed.Select(t => new { Team = t, OWP = CalculateOpponentsWinPercent(t, Matches) });

            // calculate the OOWP
            return (decimal)(teamsIvePlayedOpponentWinPercent.Sum(t => t.OWP) / teamsIvePlayed.Count());
        }

        private decimal CalculateOpponentsWinPercent(Team teamInQuestion, IEnumerable<Match> Matches)
        {
            // get each teams WP without the team in question

            //Home      Score   Away        Score
            //UConn     64      Kansas      57
            //UConn     82      Duke        68
            //Minnesota 71      UConn       72
            //Kansas    69      UConn       62
            //Duke      81      Minnesota   70
            //Minnesota 52      Kansas      62

            //UConn: ((Kansas 1.0) + (Kansas 1.0) + (Duke 1.0) + (Minnesota 0)) / (4 games) = 0.7500
            //Kansas: ((UConn 1.0) + (UConn 1.0) + (Minnesota 0.0)) / (3 games) = 0.6667
            //Duke: ((UConn 0.6667) + (Minnesota 0.0)) / (2 games) = 0.3333
            //Minnesota: ((UConn 0.6667) + (Duke 0.0) + (Kansas 0.5)) / (3 games) = 0.3889

            // get each team i've played this season (not unique)
            var teamsIvePlayed = Matches.Where(m => m.AwayTeam == teamInQuestion || m.HomeTeam == teamInQuestion).Select(s => s.HomeTeam == teamInQuestion ? s.AwayTeam : s.HomeTeam);

            // get the win percent of each team I played excluding matches with me
            var teamsIvePlayedWinPercent = teamsIvePlayed.Select(t => new 
            { 
                Team = t, 
                WP = CalculateWinPercent(t, Matches.Where(m => m.AwayTeam != teamInQuestion && m.HomeTeam != teamInQuestion), defaultHomeWinValue, defaultAwayWinValue) 
            });

            // calculate the OWP
            return (decimal)(teamsIvePlayedWinPercent.Sum(t => t.WP) / teamsIvePlayed.Count());
        }

        private decimal CalculateWinPercent(Team teamInQuestion, IEnumerable<Match> Matches, decimal HomeWinValue, decimal AwayWinValue)
        {
            // get the teams win percent - sometimes weighted based on NCAA rules

            //UConn: (0.6 + 0.6 + 1.4 + 0) / (0.6 + 0.6 + 1.4 + 1.4) = 0.6500
            //Kansas: (0 + 0.6 + 1.4) / (1.4 + 0.6 + 1.4) = 0.5882
            //Duke: (0 + 0.6) / (1.4 + 0.6) = 0.3000
            //Minnesota: (0 + 0 + 0) / (0.6 + 1.4 + 0.6) = 0.0000

            // get my wins and sum with weighting
            var wins = Matches.Where(m => m.Winner == teamInQuestion).Sum(s => s.HomeTeam == teamInQuestion ? HomeWinValue : AwayWinValue);

            // get my games played count weighted
            var gamesPlayed = Matches.Where(m => m.HomeTeam == teamInQuestion || m.AwayTeam == teamInQuestion).Sum(s => s.HomeTeam == teamInQuestion ? HomeWinValue : AwayWinValue);

            // get the WP
            return wins / gamesPlayed;
        }

        private void SetWeightingBasedOnSport(Sport Sport)
        {
            switch (Sport)
            {
                case Sport.NCAA_Basketball:
                    homeWinValue = 0.6m;
                    awayWinValue = 1.4m;
                    break;
                case Sport.NCAA_Baseball:
                    homeWinValue = 0.7m;
                    awayWinValue = 1.3m;
                    break;
                default:
                    homeWinValue = defaultHomeWinValue;
                    awayWinValue = defaultAwayWinValue;
                    break;
            }
        }
    }

    public enum Sport
    {
        NoHomeOrAwayWeighting = 1,
        NCAA_Basketball = 2,
        NCAA_Baseball = 3,
    }

    public class Team
    {
        public string Name { get; set; }
        public decimal RPI { get; internal set; }
        public decimal WP { get; internal set; }
        public decimal OWP { get; internal set; }
        public decimal OOWP { get; internal set; }
    }

    public class Match
    {
        public Team HomeTeam { get; set; }
        public Team AwayTeam { get; set; }
        public Team Winner { get; set; }
    }
}

答案 3 :(得分:1)

为什么不使用维基百科的评级百分比指数。您可以在那里找到更好的解释,但作为快速介绍,您使用以下公式:

RPI =(WP * 0.25)+(OWP * 0.50)+(OOWP * 0.25)

WP :获胜百分比获胜/ games_played

OWP :计算每个球队对手的WP的平均值,并要求所有针对该球队的比赛都从计算中删除

OOWP :每个对手的OWP的平均值。

Google Code Jam也使用了这个问题。

希望algortihm可以帮助你。

感谢维基百科。

答案 4 :(得分:0)

您可以尝试应用http://en.wikipedia.org/wiki/Elo_rating_system,主要是基于其他人使用它来发挥优势。但是,任何这样的系统都是基于真实情况的某种概率模型,你最好尝试为你的特定游戏提出一个。例如,对于足球而言,一种方法是将球队得分的进球数量建模为泊松过程,这取决于他们的进攻强度和对方的防守。一旦有了模型,就可以将其与数据相匹配,例如最大可能性。

作为有所作为的模型示例,请查看http://en.wikipedia.org/wiki/Nontransitive_dice。这是一个简单的例子,其中A通常击败B,B通常击败C,C通常击败A,这不是您所期望的简单的一维强度系统。

答案 5 :(得分:0)

===第1步===

让我们说两队A队和B队互相打了n场比赛,A队赢了m次。通过应用平坦的β分布,下次获胜的概率为:(m + 1)/(n + 2)。

如您所见,如果m和n是大数,则大致等于m / n。

===第2步===

在您的情况下,我建议采用以下策略。

设m = mp + md + mr,n = np + nd + nr

后缀p表示先行,d表示直接,r表示间接。

您可以将mp和np分别设置为1和2(假设先前为平坦)或以高级方式设置(最后详细说明)

md和nd是胜利和比赛。

先生和先生是用一些策略计算的。

最终获胜的概率是(mp + md + mr)/(np + nd + nr)。

===第3步===

如何计算mr和nr:

你可以使用某种阻尼剂。例如。如果A def C和C def B,则将其计为A对B的A胜利。对于较长的链,使用指数衰减。

p的最佳值可以使用交叉验证来计算,其中您省略了数据的某些部分并使用p来最大化该遗漏数据的概率。对于您的特定问题,我建议在一对,估计概率和与实际值进行比较之间省略游戏。

你可以使用:k * log ^ 2(s / t)作为惩罚,其中k是左对A和B之间的游戏数,s是预测的,并且是A获胜的实际概率。你也可以使用像KL分歧这样的东西。

===第4步===

重新访问设置mp和np。

你需要在同一个团队之间进行多次多场比赛才能解决这个问题。

对于每对球队计算获胜的概率并绘制它。如果它看起来平坦,使用1和2作为mp和np是好的。否则,请浏览http://en.wikipedia.org/wiki/Beta_distribution并选择最匹配的分配。

答案 6 :(得分:0)

这个问题可以通过有向图来解决。

让所有球队都成为顶点,并且在team1和team2之间的定向优势意味着team1击败了team2。

完成此操作后,您可以将图表划分为强关联组件,并独立处理每个连接的组件,因为它们在统计上是独立的。或者它们是什么? 表情

我们要问的问题, team1击败team2的机会是什么

这很容易回答,如果您要比较的球队,他们之间有直接匹配。在这种情况下,你只关心直接匹配;例如 team1击败team2多少次?你回答这个问题的方式;

(team1WinsAgainstTeam2)/(matchesPlayedBetweenThem)

这是对的吗?当我们知道teamB已经使用teamX和WON 100次时,teamA的机会是否会减少,但是teamX总是击败teamA?如果是的话,请丢弃我在这篇文章中所说的一切: - )

最终算法应如下所示:

double getOddsTeam1Winning(int team1, int team2){

    if(!isInSameConnectedComponent(team1, team2)){
        // if two teams are not in the same
        // connected component,

        // we can use heuristic,
        // we'll compare how many matches has team1 won,
        // compared to team2.

        var team1Wins = (team1Wins - team1Loses);
        var team2Wins = (team2Wins - team2Loses);

        return team1Wins / (team1Wins + team2Wins);
    }

    if(isDirectMatchBetween(team1, team2)){
        return team1WinsOverTeam2/
            totalMatchesPlayedBetweenTeam1AndTeam2;
    }

    List<double> odds= new List<double>();

    foreach(var opponentTeam in teamsThatTeam1HasPlayedWith){

        var oddsThatOponentTeamBeatsTeam2 = getOddsTeam1Winning(opponentTeam, team2);
        var oddsThatTeam1BeatsOpponentTeam = getOddsTeam1Winning(team1, opponentTeam);

        // combine them &  push them to odds list
    }

    return odds.Average(); // taking average of odds?!
}
ps,我在几分钟内把它放在一起,不完全确定它是否在数学上是正确的,但我认为这将解决你列出的原始问题中的问题,至少有一个例子:p。