Java - 加权随机算法--Demo

Java LoadBalanceUtil 负载均衡、轮询加权 https://www.cnblogs.com/vipsoft/p/19728820
Java - 加权随机算法 - 示例:https://www.cnblogs.com/vipsoft/p/19742349

A 权重 1 、B 权重 2 、C 权重 3
怎么能每次根据权重,做随机得到 A\B\C

我给你提供几种Java实现权重随机算法的方法:

方法一:数组扩展法(最简单)

import java.util.Random;

public class WeightRandom {
    private static final Random random = new Random();
    
    public static String getRandomByWeight() {
        // 权重:A(1), B(2), C(3)
        String[] items = {"A", "B", "C"};
        int[] weights = {1, 2, 3};
        
        // 计算总权重
        int totalWeight = 0;
        for (int weight : weights) {
            totalWeight += weight;
        }
        
        // 生成随机数
        int randomNum = random.nextInt(totalWeight); // 0-5
        
        // 根据权重选择
        if (randomNum < 1) {
            return "A";
        } else if (randomNum < 1 + 2) {
            return "B";
        } else {
            return "C";
        }
    }
    
    public static void main(String[] args) {
        // 测试100次,查看分布
        int aCount = 0, bCount = 0, cCount = 0;
        for (int i = 0; i < 100; i++) {
            String result = getRandomByWeight();
            switch (result) {
                case "A": aCount++; break;
                case "B": bCount++; break;
                case "C": cCount++; break;
            }
        }
        System.out.printf("A: %d次, B: %d次, C: %d次%n", aCount, bCount, cCount);
    }
}

方法二:通用工具类(推荐)

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

public class WeightRandomUtil<T> {
    private final List<WeightItem<T>> items = new ArrayList<>();
    private int totalWeight = 0;
    private final Random random = ThreadLocalRandom.current();
    
    public static class WeightItem<T> {
        T item;
        int weight;
        
        public WeightItem(T item, int weight) {
            this.item = item;
            this.weight = weight;
        }
    }
    
    public void addItem(T item, int weight) {
        items.add(new WeightItem<>(item, weight));
        totalWeight += weight;
    }
    
    public T next() {
        if (items.isEmpty() || totalWeight == 0) {
            return null;
        }
        
        int randomNum = random.nextInt(totalWeight);
        int currentWeight = 0;
        
        for (WeightItem<T> item : items) {
            currentWeight += item.weight;
            if (randomNum < currentWeight) {
                return item.item;
            }
        }
        
        return null; // 不应该到这里
    }
    
    public static void main(String[] args) {
        WeightRandomUtil<String> wr = new WeightRandomUtil<>();
        wr.addItem("A", 1);
        wr.addItem("B", 2);
        wr.addItem("C", 3);
        
        // 测试
        Map<String, Integer> countMap = new HashMap<>();
        for (int i = 0; i < 1000; i++) {
            String result = wr.next();
            countMap.merge(result, 1, Integer::sum);
        }
        
        System.out.println("测试结果:");
        countMap.forEach((k, v) -> 
            System.out.printf("%s: %d次 (占比 %.1f%%)%n", 
                k, v, v / 10.0));
    }
}

方法三:TreeMap实现(二分查找优化)

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

public class WeightRandomTreeMap<T> {
    private final TreeMap<Integer, T> weightMap = new TreeMap<>();
    private int totalWeight = 0;
    private final Random random = ThreadLocalRandom.current();
    
    public void addItem(T item, int weight) {
        totalWeight += weight;
        weightMap.put(totalWeight, item);
    }
    
    public T next() {
        if (weightMap.isEmpty()) {
            return null;
        }
        int randomNum = random.nextInt(totalWeight) + 1;
        Map.Entry<Integer, T> entry = weightMap.ceilingEntry(randomNum);
        return entry != null ? entry.getValue() : null;
    }
    
    public static void main(String[] args) {
        WeightRandomTreeMap<String> wr = new WeightRandomTreeMap<>();
        wr.addItem("A", 1);
        wr.addItem("B", 2);
        wr.addItem("C", 3);
        
        // 测试
        Map<String, Integer> countMap = new HashMap<>();
        for (int i = 0; i < 1000; i++) {
            String result = wr.next();
            countMap.merge(result, 1, Integer::sum);
        }
        
        System.out.println("测试结果:");
        countMap.forEach((k, v) -> 
            System.out.printf("%s: %d次 (占比 %.1f%%)%n", 
                k, v, v / 10.0));
    }
}

方法四:Apache Commons Math(第三方库)

如果项目中使用Apache Commons Math,可以更简洁:

import org.apache.commons.math3.distribution.EnumeratedDistribution;
import org.apache.commons.math3.util.Pair;

import java.util.ArrayList;
import java.util.List;

public class WeightRandomApache {
    public static void main(String[] args) {
        List<Pair<String, Double>> items = new ArrayList<>();
        items.add(new Pair<>("A", 1.0));
        items.add(new Pair<>("B", 2.0));
        items.add(new Pair<>("C", 3.0));
        
        EnumeratedDistribution<String> distribution = 
            new EnumeratedDistribution<>(items);
        
        // 测试
        for (int i = 0; i < 10; i++) {
            System.out.print(distribution.sample() + " ");
        }
    }
}

建议

  1. 简单场景:使用方法一,最直观
  2. 通用需求:使用方法二,可重用性好
  3. 大数据量:使用方法三(TreeMap),查找效率高(O(log n))
  4. 已有Apache Commons:使用方法四,代码最简洁

你的权重配置(A:1, B:2, C:3)意味着:

  • A的概率:1/6 ≈ 16.7%
  • B的概率:2/6 ≈ 33.3%
  • C的概率:3/6 = 50%
posted @ 2026-03-20 08:24  VipSoft  阅读(2)  评论(0)    收藏  举报