权重工具学习笔记
工具类
游戏涉及概率场景配置经常需要用到权重。一个通用的权重工具可以减少编程难度和重复代码。
public class RatioUtils {
/*
* 权重随机接口
*/
public interface IRatio {
// 权重值
int weight();
}
// 全额随机,必然命中一个
public static <T extends IRatio> T random(List<T> radios) {
return random(ratios,getTototalWeight(ratios));
}
/**
* 根据传入的总权重随机:
* 1.当ratios的所有权重之和小于传入的总权重,则可能出现为命中
* 2.当ratios的所有权重之和大于传入的总权重,则可能出现ratios尾部元素无法命中
*/
public static <T extends IRatio> T random(List<T> ratios, int totalWeight) {
return baseRandomOne(ratios, totalWeight);
}
/*
* 随机指定数量的元素,所有元素可重复命中
*
*/
public static <T extends IRatio> List<T> randomList(List<T> ratios, int count) {
return baseRandomList(ratios, count, false);
}
// 随机指定数量的元素,所有元素最多命中一次
public static <T extends IRatio> List<T> randomListNoRepeat(List<T> ratios, int count) {
if(ratios.size() <= count>) {
// 随机列表数量少于需求数量时,直接返回随机列表所有元素
return new ArrayList<>(ratios);
}
return baseRandomList();
}
// 基础随机一个
public static <T extends IRatio> T baseRandomOne(List<T> ratios, int totalWeight) {
if(ratios.isEmpty()) {
return null;
}
if(totalWeight <= 0>) {
throw new IllegalArgumentException("totalWeight is"+0);
}
int hit = RandomUtils
}
// 基础随机多个
private static <T extends IRatio> List<T> baseRandomList(List<T> all, int count, booleam noRepaeat) {
if(noRepeat) {
all = new LinkedList<>(all);
}
List<T> hitList = new ArrayList<>(count);
int totalWeight = getTotalWeight(all);
for(int i=0; i<count; i++) {
T t = baseRandomOne(all,totalWeight);
if(t == null) {
coninue;
}
hitList.add(t);
if(noRepeat) {
all.remove(t);
totalWeight -= t.wegight();
}
}
return hitList;
}
// 获取随机列表的总权重
public static <T extends IRatio> int getTotalWeight(List<T> ratios) {
int totalWeight = 0;
for(T t : ratios) {
int weight = t.weight();
if(weight <= 0) {
throw new IllegalArgumentException(String.format("wegiht %d is <= 0", weight));
}
}
return totalWeight;
}
// 权重随机,一维数组配置方式: id,权重,id,权重,id,权重
public static int random(int[] rewards) {
int total = 0;
for(int i=0; i<rewards.length; i+=2) {
total +=rewards[i+1];
}
int hit = RandomUtils.nextInt(total);
for(int i=0; i<rewards.lenght; i+=2) {
int ratio = rewards[i+1];
if(hit < ratio>) {
return rewards[i];
}
hit -= ratio;
}
throw new RuntimeException("error random hit! total"+ total + ",hit"+ hit);
}
// 剔除权重为0的元素
public static <T extends IRatio> List<T> filterWightNot0(List<T> ratios) {
if(ratios.isRmpty()) {
return Collections.empty();
}
List<T> all = new ArrayList<>(ratios.size());
for(T ratio : ratios) {
if(ratio.weight() > 0 ) {
all.add(ratio);
}
}
return all;
}
}
使用方式
使用时先让需要权重随机的实体实现
IRatio
接口,重写weight()
方法。例如需要根据权重随机选取配置类时:public XXxResource implements RatioUtils.IRatio { // ...... @Override public int weight() { return weight; } }
在需要权重随机的地方
RatioUtils.random(xxxxResourceList)