Java 从反向加权贴图中选择随机值

Java 从反向加权贴图中选择随机值,java,random,Java,Random,我正在为我正在开发的一个刽子手游戏使用一个带有合适单词的地图。映射中的整数存储选择单词的时间,因此在开始时映射如下所示: alabanza 0 esperanza 0 comunal 0 aprender 0 .... 玩了几次之后,地图会像这样 alabanza 3 esperanza 4 comunal 3 aprender 1 .... 我想随机选择下一个单词,但选择较少的单词被选择的概率更大 我读过,但情况正好相反 我还认为我可以使用一个重复单词的列表(一个单词在列表中出现的次数越多

我正在为我正在开发的一个刽子手游戏使用一个带有合适单词的地图。映射中的整数存储选择单词的时间,因此在开始时映射如下所示:

alabanza 0
esperanza 0
comunal 0
aprender 0
....
玩了几次之后,地图会像这样

alabanza 3
esperanza 4
comunal 3
aprender 1
....
我想随机选择下一个单词,但选择较少的单词被选择的概率更大

我读过,但情况正好相反

我还认为我可以使用一个重复单词的列表(一个单词在列表中出现的次数越多,被选择的概率就越大),但我只做到了这一点:

在那之后,我选择了Lajos Arpad或Ahmad Shahwan给出的食谱中的单词。 如果游戏被玩了很多次,所有的概率都趋向于1,但我的情况不是这样


感谢所有回答的人。

我不会提供确切的代码,但会提供基本的想法

  • 迭代
    wordList.values()
    以查找最大权重
    M
    和权重之和
    S

  • 现在,让每个单词
    w
    都有被选择
    M+1-wordList的可能性(比如概率,但它们不必求和为1)。获取(w)
    ,这样一个权重
    1
    的单词被选择的可能性是权重
    M
    的单词的两倍

  • 可能性的总和将是
    (M+1)*wordList.size()-S
    (这就是为什么我们需要
    S
    )。在0和该总和之间选择一个随机数
    R

  • 迭代
    wordList.entrySet()
    ,在执行过程中对可能性进行求和。当总和通过
    R
    时,这就是您想要的单词


  • 你的地图值就是你的权重

    您需要选择一个小于权重总和的整数

    选择每个字符串条目及其权重。当权重和通过随机整数时,您位于字符串上

    这将为您提供:

    public static void main(String ... args){
        Map<String, Integer> wordList = new HashMap<>();
        wordList.put("foo", 4);
        wordList.put("foo2", 2);
        wordList.put("foo3", 7);
    
        System.out.println(randomWithWeight(wordList));
    }
    
    public static String randomWithWeight(Map<String, Integer> weightedWordList) {
        int sum = weightedWordList.values().stream().collect(Collectors.summingInt(Integer::intValue));
    
        int random = new Random().nextInt(sum);
        int i = 0;
        for (Map.Entry<String, Integer> e : weightedWordList.entrySet()){
            i += e.getValue();
            if (i > random){
                return e.getKey();
            }
        }
        return null;
    }
    
    publicstaticvoidmain(字符串…参数){
    Map wordList=newhashmap();
    字表。放入(“foo”,4);
    字表。put(“foo2”,2);
    字表.put(“foo3”,7);
    System.out.println(randomWithWeight(字表));
    }
    公共静态字符串randomWithWeight(映射权重字列表){
    int sum=weightedWordList.values().stream().collect(Collectors.summingit(Integer::intValue));
    int random=new random().nextInt(总和);
    int i=0;
    对于(Map.Entry e:weightedWordList.entrySet()){
    i+=e.getValue();
    如果(i>随机){
    返回e.getKey();
    }
    }
    返回null;
    }
    
    试试这个:

    import java.util.Map;
    import java.util.HashMap;
    import java.util.Random;
    
    public class MyClass {
        public static void main(String args[]) {
            Map<String, Integer> wordList = new HashMap<>();
            wordList.put("alabanza", 3);
            wordList.put("esperanza", 4);
            wordList.put("comunal", 3);
            wordList.put("aprender", 1);
    
            Map<String, Integer> results = new HashMap<>(4);
            for (int i = 0; i < 100; i++) {
                String name = randomize(wordList);
                Integer old = results.getOrDefault(name, 0);
                results.put(name, old + 1);
            }
    
            for (Map.Entry<String, Integer> e : results.entrySet()) {
                System.out.println(e.getKey() + "\t" + e.getValue());
            }
        }
    
        private static String randomize(Map<String, Integer> wordList) {
            final Integer sum = wordList.values().stream().reduce(Integer::sum).orElse(0);
            final int grandSum = (wordList.size() - 1) * sum;
            final int random = new Random().nextInt(grandSum + 1);
    
            int index = 0;
            for (Map.Entry<String, Integer> e: wordList.entrySet()) {
                index += (sum - e.getValue());
                if (index >= random) {
                    return e.getKey();
                }
            }
            return null;
        }
    }
    
    import java.util.Map;
    导入java.util.HashMap;
    导入java.util.Random;
    公共类MyClass{
    公共静态void main(字符串参数[]){
    Map wordList=newhashmap();
    单词表。put(“alabanza”,3);
    单词表。put(“世界语”,4);
    单词表。put(“comunal”,3);
    单词表。put(“aprender”,1);
    映射结果=新的HashMap(4);
    对于(int i=0;i<100;i++){
    字符串名称=随机化(单词列表);
    整数old=results.getOrDefault(名称,0);
    结果:put(名称,旧+1);
    }
    对于(Map.Entry e:results.entrySet()){
    System.out.println(e.getKey()+“\t”+e.getValue());
    }
    }
    私有静态字符串随机化(映射字列表){
    final Integer sum=wordList.values().stream().reduce(Integer::sum).orElse(0);
    最终整数grandum=(wordList.size()-1)*和;
    final int random=new random().nextInt(grandum+1);
    int指数=0;
    对于(Map.Entry e:wordList.entrySet()){
    索引+=(求和-e.getValue());
    如果(索引>=随机){
    返回e.getKey();
    }
    }
    返回null;
    }
    }
    
    Out put是在100次试验中选择名称的次数:

    
    裙座37
    阿拉班扎25
    社区23
    世界语15
    


    您可以自己尝试。

    为了简单起见,让我们假设您有一个名为
    引用
    的数组,其中包含
    int
    元素(您可以轻松地将其转换为数据结构)

    现在,让我们找出最大值:

    int max = 0;
    
    for (int i = 0; i < occurrences.length; i++) {
        if (max < occurrences[i]) max = occurrences[i];
    }
    
    现在,让我们为值为0的项目赋予
    max
    权重,为发生过一次的项目赋予
    max-1
    权重,依此类推(自从我们增加
    max
    之后,没有任何项目的权重为0):


    结果是
    出现[resultIndex]

    首先,所有整数值都是零(因为还没有选择任何单词),并且您的方法抛出一个java.lang.IllegalArgumentException:bound必须为正,因为和为0。这是我遇到的问题之一。当没有选择任何单词时(所有权重均为零),可能在开始时w有问题。例如,我是否应该将其更改为1,并在选择单词时减小权重,每次选择一个单词时用双倍数除以2?在我看来,一切都是从一开始就开始的:
    M
    S
    都是0,因此每个单词的似然度为1,似然度之和为
    wordList.size()
    。另一种简单的方法是对
    w
    1/(wordList.get(w)+1)
    。在您的示例(0,0,0,0)(3,4,3,1)中,您希望每个条目被选中的概率是多少?这是一个问题。我不能用一种“自然”的方式来定义这种可能性,因为它的工作方式是超然的。我终于选择了另一种方法,但是谢谢
    import java.util.Map;
    import java.util.HashMap;
    import java.util.Random;
    
    public class MyClass {
        public static void main(String args[]) {
            Map<String, Integer> wordList = new HashMap<>();
            wordList.put("alabanza", 3);
            wordList.put("esperanza", 4);
            wordList.put("comunal", 3);
            wordList.put("aprender", 1);
    
            Map<String, Integer> results = new HashMap<>(4);
            for (int i = 0; i < 100; i++) {
                String name = randomize(wordList);
                Integer old = results.getOrDefault(name, 0);
                results.put(name, old + 1);
            }
    
            for (Map.Entry<String, Integer> e : results.entrySet()) {
                System.out.println(e.getKey() + "\t" + e.getValue());
            }
        }
    
        private static String randomize(Map<String, Integer> wordList) {
            final Integer sum = wordList.values().stream().reduce(Integer::sum).orElse(0);
            final int grandSum = (wordList.size() - 1) * sum;
            final int random = new Random().nextInt(grandSum + 1);
    
            int index = 0;
            for (Map.Entry<String, Integer> e: wordList.entrySet()) {
                index += (sum - e.getValue());
                if (index >= random) {
                    return e.getKey();
                }
            }
            return null;
        }
    }
    
    int max = 0;
    
    for (int i = 0; i < occurrences.length; i++) {
        if (max < occurrences[i]) max = occurrences[i];
    }
    
    max++;
    
    int totalWeight = 0;
    
    for (int j = 0; j < occurrences.length; j++) {
        totalWeight += max - occurrences[j];
    }
    
    int resultIndex = -1;
    
    for (int k = 0; (resultIndex < 0) && k < occurrences.length; k++) {
        if (r <= max - occurrences[k]) resultIndex = k;
        else r -= max - occurrences[k];
    }