ELO(埃洛匹配分制度)Java篇

本文介绍了ELO算法的基本原理,包括预期胜负值的计算、程序中的精度调整策略,以及在Java中如何通过四舍五入和偏向0取整确保积分变动。重点讲解了如何解决因精度问题导致的积分计算不一致问题,通过实例演示了ELO算法在实际比赛中的积分更新过程。
摘要由CSDN通过智能技术生成

1. ELO算法数学实现介绍

其中表达式各字母含义:

EA:预期A玩家的胜负值

EB:预期B玩家的胜负值

RA:A玩家当前的积分
RB:B玩家当前的积分

SA:实际胜负值(A VS B),胜=1,平=0.5,负=0

SB:实际胜负值(B VS A),胜=1,平=0.5,负=0

K :常量系数

2. 程序分析

2.1 从数学的角度来看,EA + EB = 1,但实际在程序计算中,很难获取精确的EA或EB,可能导致EA + EB != 1,所以我们这里选择计算出EA,而EB由1 - EA获取,这样可以修正一定的数据误差。再者,我们知道,在ELO算法中,两个玩家的得失分总是相抵的,也就是说,两个玩家的总分是不变的(在积分不存在限制的情况下),这样我们只要得出一个玩家的本次所增加的积分(失败表示增加的积分为负),便可求出其对手的积分变化情况。

2.2 在现实的业务场景中,积分总是以整数方式存在着,所以我们对计算所得的积分进行处理,数字从浮点型变成整型一般方式有:四舍五入,去尾及其它方式,在我看来,玩家竞技对于胜利的一方,不论双方的积分相差较大或较小,能应该增加积分,基于此考虑,我选择偏离0取整(即正数向上取整,负数向下取整),以保证每次不为和局的竞技都会产生积分变动。

2.3 另一个在实现中遇到的问题,是由2.1使用的方式引起的,即可能产生由公式计算而来的EB和由1 - EA计算而来的EB之间存在错误(精度问题),这将导致出现相同数据计算结果不同的问题。举个例子,由参数(A的积分,B的积分,A胜B)和参数(B的积分,A的积分,B负A)计算出来的结果是不一样的,就比如说,由参数(A的积分,B的积分,A胜B)算出的EA是0.516,而由(B的积分,A的积分,B负A)算出来的EB是0.481,这是有可能的,因为精度及取舍问题产生,所以,我们必须解决这个结果不可重复的问题。解决方式至少有两种,第一次,我们统一由胜利的一方计算出变动积分,但这个时候还要另外处理和局的情况。所以我使用另一种方式,统一由积分高的一方计算出变动积分,以下是JAVA代码实现:
 

import java.math.BigDecimal;

public class EloMatch {
	public static void main(String[] args) {
		System.out.println("我启动了");
		int aScore = 100;
		int bScore = 50;
		float sa = 0;
		int k = 16;
		EloResult eloResult = EloUtils.rating(aScore, bScore, sa, k, false);
		if (sa == 1) {
			System.out.println("A 赢了比赛后得分:" + eloResult.getRa());
			System.out.println("B 输了比赛后得分:" + eloResult.getRb());
		} else if (sa == 0) {
			System.out.println("A 输了比赛后得分:" + eloResult.getRa());
			System.out.println("B 赢了比赛后得分:" + eloResult.getRb());
		}
	}
}

class EloUtils {
	private final static BigDecimal DONE = new BigDecimal("1.0");
	private final static BigDecimal D400 = new BigDecimal("400.0");

	/*
	 * 通过ELO算法计算比赛得分
	 * ra        玩家A本轮比赛前得分
	 * rb        玩家B本轮比赛钱得分
	 * sa        A VS B 结果:胜利 1,平 0.5,失败 0  sa代表A玩家胜利或失败结果,统一由分数高的一方计算出积分变动的情况,若分数相同选择其中一方计算
	 * k         极限值,代表理论上最多可以赢的分数和失去的分数
	 * Limit     是否开启下限为0的限制
	 * return    比赛结束后返回A,B玩家的得分和失分
	 */
	public static EloResult rating(int ra, int rb, float sa, int k, boolean limit) {
		//统一由分数高的一方计算出积分变动的情况
		if (ra > rb) {
			EloResult result = rating(rb, ra, 1.0f - sa, k, limit);
			int temp = result.getRa();
			result.setRa(result.getRb());
			result.setRb(temp);
			return result;
		}

		BigDecimal ea = DONE.divide(DONE.add(new BigDecimal(Math.pow(10, new BigDecimal(rb - ra).divide(D400, 6, BigDecimal.ROUND_HALF_UP).doubleValue()))), 6, BigDecimal.ROUND_HALF_UP);
		double score = new BigDecimal(k).multiply(new BigDecimal(sa).subtract(ea)).doubleValue();

		//为正数变动积分向上取证
		int scoreI = (int) Math.ceil(score);
		if (score < 0d) { //为负数变动积分向下取证
			scoreI = (int) Math.floor(score);
		}

		EloResult elo = new EloResult();
		elo.setRa((ra + scoreI < 0 && limit) ? 0 : ra + scoreI);
		elo.setRb((rb - scoreI < 0 && limit) ? 0 : rb - scoreI);

		return elo;
	}
}

class EloResult {
	/*
	 * 玩家A本轮比赛后得分
	 */
	private int ra;

	/*
	 * 玩家B本轮比赛后得分
	 */
	private int rb;

	public int getRa() {
		return ra;
	}

	public void setRa(int ra) {
		this.ra = ra;
	}

	public int getRb() {
		return rb;
	}

	public void setRb(int rb) {
		this.rb = rb;
	}
}


 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jerry Nan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值