java案例如何分配不同场景的权重?

wen java案例 14

Java 案例:不同场景的权重分配方案

下面我给你一套可直接落地的权重分配方案,覆盖 5 种典型场景,每种都给出核心思路 + Java 代码。

java案例如何分配不同场景的权重?


场景总览

场景 典型业务 推荐算法
固定权重 页面模块占比、资源配额 静态配置
按比例动态分配 AB 测试流量、灰度发布 比例/百分比
加权随机 广告投放、商品推荐 前缀和 + 二分 / 别名法
加权轮询 负载均衡、任务调度 平滑加权轮询
多因素综合评分 风控评分、排序 归一化 + 加权求和

场景 1:固定权重(配置化)

public class FixedWeightConfig {
    // 场景 -> 权重配置
    private static final Map<String, Map<String, Double>> CONFIG = Map.of(
        "homepage", Map.of("banner", 0.5, "recommend", 0.3, "news", 0.2),
        "search",   Map.of("ad", 0.4, "organic", 0.6)
    );
    public static double getWeight(String scene, String item) {
        return CONFIG.getOrDefault(scene, Map.of()).getOrDefault(item, 0.0);
    }
}

适合:权重几乎不变、需要审计追溯的场景。


场景 2:按比例分配流量(AB 测试)

public class TrafficAllocator {
    private final NavigableMap<Double, String> buckets = new TreeMap<>();
    private double total = 0;
    public TrafficAllocator(Map<String, Double> weights) {
        weights.forEach((key, w) -> {
            total += w;
            buckets.put(total, key);
        });
    }
    public String pick(double random) {
        return buckets.ceilingEntry(random * total).getValue();
    }
    public static void main(String[] args) {
        TrafficAllocator alloc = new TrafficAllocator(
            Map.of("groupA", 30.0, "groupB", 50.0, "groupC", 20.0)
        );
        Random r = new Random();
        long[] cnt = new long[3];
        for (int i = 0; i < 100_000; i++) {
            String g = alloc.pick(r.nextDouble());
            if ("groupA".equals(g)) cnt[0]++;
            else if ("groupB".equals(g)) cnt[1]++;
            else cnt[2]++;
        }
        System.out.printf("A=%d, B=%d, C=%d%n", cnt[0], cnt[1], cnt[2]);
    }
}

适合:灰度发布、实验分组。用户体验一致性可用 userId.hashCode() 代替随机数。


场景 3:加权随机(推荐/广告)

方案 A:前缀和 + 二分(O(log n) 查询)

public class WeightedRandom {
    private final double[] prefix;
    private final String[] items;
    private final Random random = new Random();
    public WeightedRandom(Map<String, Double> weights) {
        int n = weights.size();
        this.items = weights.keySet().toArray(new String[0]);
        this.prefix = new double[n];
        double sum = 0;
        for (int i = 0; i < n; i++) {
            sum += weights.get(items[i]);
            prefix[i] = sum;
        }
    }
    public String next() {
        double target = random.nextDouble() * prefix[prefix.length - 1];
        int idx = Arrays.binarySearch(prefix, target);
        if (idx < 0) idx = -idx - 1;
        return items[Math.min(idx, items.length - 1)];
    }
}

方案 B:别名法(Alias Method,O(1) 查询)

public class AliasMethod {
    private final int[] alias;
    private final double[] prob;
    private final String[] items;
    private final Random random = new Random();
    public AliasMethod(Map<String, Double> weights) {
        int n = weights.size();
        items = weights.keySet().toArray(new String[0]);
        double[] p = new double[n];
        double sum = weights.values().stream().mapToDouble(d -> d).sum();
        for (int i = 0; i < n; i++) p[i] = weights.get(items[i]) * n / sum;
        alias = new int[n];
        prob = new double[n];
        Deque<Integer> small = new ArrayDeque<>(), large = new ArrayDeque<>();
        for (int i = 0; i < n; i++) (p[i] < 1 ? small : large).push(i);
        while (!small.isEmpty() && !large.isEmpty()) {
            int s = small.pop(), l = large.pop();
            prob[s] = p[s];
            alias[s] = l;
            p[l] = (p[l] + p[s]) - 1;
            (p[l] < 1 ? small : large).push(l);
        }
        while (!large.isEmpty()) prob[large.pop()] = 1;
        while (!small.isEmpty()) prob[small.pop()] = 1;
    }
    public String next() {
        int i = random.nextInt(items.length);
        return random.nextDouble() < prob[i] ? items[i] : items[alias[i]];
    }
}

选型:场景候选少(<100)用 A;候选多、高频调用(广告召回/推荐)用 B。


场景 4:平滑加权轮询(负载均衡/任务调度)

public class SmoothWeightedRoundRobin {
    static class Node {
        String name;
        int weight;
        int current = 0;
        Node(String name, int weight) { this.name = name; this.weight = weight; }
    }
    private final List<Node> nodes;
    private int totalWeight;
    public SmoothWeightedRoundRobin(Map<String, Integer> weights) {
        nodes = new ArrayList<>();
        weights.forEach((k, v) -> { nodes.add(new Node(k, v)); totalWeight += v; });
    }
    public synchronized String next() {
        Node best = null;
        for (Node n : nodes) {
            n.current += n.weight;
            if (best == null || n.current > best.current) best = n;
        }
        best.current -= totalWeight;
        return best.name;
    }
    public static void main(String[] args) {
        var lb = new SmoothWeightedRoundRobin(Map.of("A", 5, "B", 1, "C", 2));
        for (int i = 0; i < 16; i++) System.out.print(lb.next() + " ");
        // 输出:A B A C A A C A A B ...
    }
}

相比朴素加权轮询,避免了大权重节点连续命中,分布更均匀。


场景 5:多因素综合评分(风控/排序)

public class ScoreCalculator {
    // 各维度权重
    private static final Map<String, Double> WEIGHTS = Map.of(
        "credit", 0.40,
        "behavior", 0.30,
        "device", 0.20,
        "history", 0.10
    );
    /** 各维度原始分需先归一化到 0~100 */
    public static double score(Map<String, Double> rawScores) {
        return WEIGHTS.entrySet().stream()
            .mapToDouble(e -> {
                double v = rawScores.getOrDefault(e.getKey(), 0.0);
                double normalized = Math.max(0, Math.min(100, v)); // 截断
                return normalized * e.getValue();
            })
            .sum();
    }
    public static void main(String[] args) {
        double s = score(Map.of(
            "credit", 85.0, "behavior", 70.0, "device", 60.0, "history", 90.0
        ));
        System.out.println("综合分:" + s);  // 77.0
    }
}

关键点:

  • 各维度必须先归一化再乘权重,否则量纲不同会导致权重失效
  • 权重和最好 = 1,便于解读
  • 复杂场景可用 AHP 层次分析法 或 熵权法 自动确定权重

选型决策树

权重是否恒定?
├── 是 → 硬编码/配置中心(场景1)
└── 否
    ├── 需要严格按比例(AB/灰度)→ 前缀和桶 + 稳定hash(场景2)
    ├── 只需概率分布 → 加权随机(场景3)
    ├── 需均匀轮转(负载)→ 平滑加权轮询(场景4)
    └── 多维综合评估 → 归一化 + 加权求和(场景5)

生产环境建议

  1. 权重配置外置(Nacos/Apollo),避免重启
  2. 权重变化要原子切换:用 volatile 引用整体替换对象,或用 AtomicReference
  3. 监控实际命中比例,与配置权重偏差 >5% 告警
  4. 兜底:所有权重为 0 或空时,走默认策略(如均匀分配)
  5. 可复现性:AB 场景用 userId 的稳定哈希,同一用户始终进同一组

如果你有具体的业务场景(比如推荐系统的 CTI 加权、限流配额分配、多租户资源分配),告诉我,我可以给出更贴合的代码。

友情链接

欧易欧易欧易欧易欧易欧易欧易欧易直播帮篮球控NBA帮比赛虎聊足球

抱歉,评论功能暂时关闭!

友情链接攻略窝 · 礼包窝 · 配置窝 · 换算窝 · 算法窝 · 查文窝 · 手册窝 · 问答窝 · 全文搜 · 手册库
相关站点礼包窝 · 查文窝