【问题标题】:Dynamic Weighting Randomization动态加权随机化
【发布时间】:2015-02-28 03:57:36
【问题描述】:

我试图通过从一组预定义的图块中绘制一条路径,每个图块都具有某种“类型”。但是,我想对随机化进行加权,以便每个“类型”以相同的机会开始,但某些条件会导致特定“类型”的权重(例如经常被选择)增加或减少。

我对随机化算法不是很熟悉。是否有任何预先存在的算法与我的设置非常匹配?

更新

@CandiedOrange 这就是我的想象,就代码的一般设计而言,你在解释什么。

假设我们有一个名为 Letter 的枚举,其中包含几个不同的元素,简称为 A、B、C、D 和 E。然后假设我们有一个整数数组,就你的纸牌类比而言,它们表示:每种字母的数量。该数组将在每个条目中使用值 1 进行初始化。选择一个字母后(从而将其从“牌组”中删除),我检查相应的条目是否已达到 0,在这种情况下,我将所有值增加 1,就好像我在新牌组中洗牌一样,然后选择另一个字母。

这听起来像你想要解释的吗?

如果是这样,我想我可以修改这个设计以满足我的需要。每次选择一个字母并将其从套牌中移除时,我都可以检查选择该特定字母是否会触发任何条件。如果是这样,我可以添加或减去数组中的特定值来修改概率。

【问题讨论】:

  • 路径到底是什么意思?一条穿过花园的小路,铺着不同“类型”的瓷砖可供行走?
  • 想想 2D 地牢地图,但我没有放置房间并将它们与走廊连接起来,而是使用一组代表房间或走廊的瓷砖。

标签: java random weighted


【解决方案1】:

为每种类型创建一个概率值数组(范围从 0.0 到 1.0)。 “滚动”一个随机数并检查是否大于类型的值。

您的“某些条件”会增加或减少某种类型的概率值。

【讨论】:

  • 然后我会根据滚动值对已选择的那些进行非加权选择吗?
【解决方案2】:

我最近也有类似的需要制作棒球比赛。有各种各样的随机选择,所以我创建了两个实用程序类来帮助简化我的代码。

考虑以下关于棒球击球时可能的轨迹的枚举。

enum Trajectory {
    FLY, GROUNDER, LINE_DRIVE, POPUP;
}

使用我的实用程序类,通过以下代码 sn-p 来确定随机轨迹。

    List<Probability<Trajectory>> odds = Arrays.asList(
        Probability.of(Trajectory.FLY, 0.5),
        Probability.of(Trajectory.GROUNDER, 0.2),
        Probability.of(Trajectory.LINE_DRIVE, 0.3));

    Trajectory result = Luck.determine(odds);

如果您喜欢这种设计,这里是所涉及的两个类的简化但功能齐全的版本。

Probability.java

public class Probability<T>
{
    public static <T> Probability<T> of(T value, double chance) {
        return new Probability<T>(value, chance);
    }

    private final T value;
    private final double chance;

    public Probability(T value, double chance) {
        this.value = value;
        this.chance = chance;
    }

    T getValue() {
        return value;
    }

    double getChance() {
        return chance;
    }

    @Override
    public String toString() {
        return new StringBuilder(20)
            .append(value).append(": ").append(chance)
            .toString();
    }
}

Luck.java

import java.math.*;
import java.util.*;

public class Luck
{
    private static final MathContext CONTEXT = new MathContext(5);

    /**
     * Make a random determination from a list of probabilities based
     * on the fractional chance of each probability.
     * @throws IllegalArgumentException if the total chance of the
     *             probabilities is not within 1/10000th of 1.
     */
    public static <X, T extends Probability<X>> X determine(List<T> probabilities) {
        double chance = 0f;
        for (Probability<X> probability : probabilities) {
            chance += probability.getChance();
        }
        BigDecimal total = new BigDecimal(chance).round(CONTEXT);
        double determination = Math.random();
        // System.out.println(new StringBuilder(128).append(probabilities)
        //    .append(" :random: ").append(determination));
        if (BigDecimal.ONE.compareTo(total) != 0) {
            throw new IllegalArgumentException("probabilities' chances must total 1");
        }
        chance = 0f;
        for (Probability<X> probability : probabilities) {
            chance += probability.getChance();
            if (determination < chance) {
                return probability.getValue();
            }
        }
        return probabilities.get(0).getValue();
    }
}

Main.java

import java.util.*;

public class Main
{
    enum Trajectory {
        FLY, GROUNDER, LINE_DRIVE, POPUP;
    }

    public static void main(String[] args) {
        List<Probability<Trajectory>> odds = Arrays.asList(
            Probability.of(Trajectory.FLY, 0.5),
            Probability.of(Trajectory.GROUNDER, 0.2),
            Probability.of(Trajectory.LINE_DRIVE, 0.3));

        Trajectory result = Luck.determine(odds);

        // run the odds a hundred times to see how they work out
        Map<Trajectory, Integer> counts = new HashMap<Trajectory, Integer>();
        for (Trajectory trajectory: Trajectory.values()) {
            counts.put(trajectory, 0);
        }
        int i = 0;
        do {
            counts.put(result, counts.get(result) + 1);
            result = Luck.determine(odds);
            i++;
        } while (i < 100);
        System.out.println(counts);
    }
}

输出

{FLY=50, GROUNDER=19, LINE_DRIVE=31, POPUP=0}

【讨论】:

    【解决方案3】:

    如果被选中会影响概率,那么你正在做一个没有替换的过程。从一副牌中选择一张牌,您不能再次选择它。把它放回去,你可能会。这是一个替换的过程。

    在这两个极端之间的某个地方,永远不会再选择它和喜欢你永远不会选择它,这听起来像是你想成为的地方。有一种非常简单的方法可以到达那里。使用多于一副牌。使用无限数量的套牌,这与将您选择的卡放回相同。

    这意味着如果您接受@MitchWheat 的建议,并在开始时为您的瓷砖类型赋予 1.0 的权重,每次选择一种类型时减去 0.5,这与您拿走两套完整的瓷砖并洗牌时完全相同他们在一起。为什么? 1 / 0.5 == 2。

    知道这些是相同的可能会让您找到更多可以满足您需要的现有算法。例如Fisher-Yates shuffle:

    Shuffle a List

    Shuffle a Map

    http://www.java2s.com/Tutorials/Java/java.util/Collections/Java_Collections_shuffle_List_lt_gt_list_Random_rnd_.htm

    如果您需要能够提供无限数量的瓷砖,只需在耗尽后重新进货并重新排列列表即可。

    在这种方法中,图块仅根据每种类型在列表中出现的次数来加权。

    更新

    我不喜欢一副纸牌的类比,因为它暗示 在下一个“甲板”之前,已选择的瓷砖不能再次使用 被洗牌了。

    如果你一次只洗一副牌,这是真的。在洗牌前添加更多套牌或什至只是更多卡片可以让您控制起始“权重”,但仍会随着选择的做出和移除而调整。

    一旦选择了一种类型的图块,其中的每个图块 应该有平等的机会被选中

    我不知道该怎么做。所选类型中的每个图块?这是没有意义的,除非两个相同类型的瓷砖以某种方式可以区分。对于纸牌,我会说选择哪张梅花 A 并不重要。

    如果您的意思是希望类型的选择始终相等,那么您根本不需要概率权重。

    洗牌的类比是为了给出一些关于如何根据选择调整权重的观点。如果您还想根据其他因素更改权重,您仍然可以通过添加或删除图块来实现。

    当然,您可以坚持使用 0.0 到 1.0 的权重。我只是指出,唯一能让你受益的是能够将权重设置为无理数。任何可以表示为分数的权重集合都可以建模为一摞洗牌。

    【讨论】:

    • 我不喜欢一副牌的类比,因为这意味着在下一个“牌组”被洗入之前,已经选择的牌不能再次使用。一旦一种牌已被选中,其中的每个图块都应该有平等的机会被选中。
    • 好吧,我有点误解了你当时想说的意思。我认为这个类比对我来说并不直观,但我现在明白你在说什么。我正在根据我对如何实施的理解更新我的帖子。
    猜你喜欢
    • 1970-01-01
    • 2020-06-20
    • 1970-01-01
    • 2022-07-06
    • 1970-01-01
    • 2010-12-18
    • 2022-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多