【问题标题】:How do I generate random letters in java based on probability?如何根据概率在java中生成随机字母?
【发布时间】:2012-09-03 17:59:05
【问题描述】:

我无法根据概率生成随机字母。

例如,字母 J、K、Q、Y、Z 各有 1/96 的发生概率。类似的过程(概率更高)用于其他字母。

谁能教我怎么做?

具体编辑:我正在编写一个名为“getRandomLetter”的方法,它根据概率分数返回一个随机字母的字符。

【问题讨论】:

  • 你想要一个只返回一个随机选择的字母的方法吗?还是一串?还是数组?请具体说明您想要做什么。
  • 方法很多。你试过什么吗?您想吐出能够为每个字母设置不同概率的随机字母吗?

标签: java random probability


【解决方案1】:

最令人愉悦的解决方案需要一个紧凑的容器来存储给定字母的出现概率。我建议使用 HashMap 作为概率函数(离散分布函数)。像这样:

HashMap<Character, Double> map = new HashMap<Character, Double>();
for(Character c : {'J', 'K', 'Q', 'Y', 'Z'}) {
    map.put(c, 1.0 / 96.0);
}
// and so on

为了方便起见,最好确保所有概率的总和等于1.0,但这些数字可以被视为概率权重并在最后进行归一化。你明白了吧?

纯数学方法需要创建一个累积分布函数,将其还原,然后明确使用该函数。通过这种方式,您可以提供一种生成具有几乎任何概率分布的任何随机值的解决方案。

让我们尝试一下:

double sum = 0.0, partialSum = 0.0;
HashMap<Double, Character> dist = new HashMap<Double, Character>();
for(Entry<Character, Double> entry : map.entrySet()) {
    sum += entry.getValue(); // for normalization purpose, if you are really sure
    // that all the probabilities sum up to 1.0, then the first loop is redundant
}
for(Map.Entry<Character, Double> entry : map.entrySet()) {
    dist.put(partialSum / sum, entry.getKey());
    partialSum += entry.getValue(); // the cumulative probability here
}

现在使用地图只需调用

Random r = new Random();
...
dist.get(r.nextDouble());

【讨论】:

    【解决方案2】:

    从具有特定概率的离散元素集中进行选择的典型方法是选择一个随机浮点数并找出它所在的范围。我将用一个例子来解释。假设您在三个字母 A、B 和 C 中进行选择,概率分别为 0.255、0.407 和 0.338。您将计算一个介于 0 和 1 之间的随机数

    double r = Math.random();
    

    并首先将其与 0 到 0.255 的范围进行比较:

    if (r < 0.255) {
        return 'A';
    }
    

    然后到 0.255 到 (​​0.255 + 0.407) 的范围:

    else if (r < 0.662) {
        return 'B';
    }
    

    如果不是这两个,它必须是'C'

    else {
        return 'C';
    }
    

    如果您要对所有 26 个字母表进行此操作,那么写出 if-else 语句的所有 26 个大小写会很痛苦。您可以提前做的是准备一个字符数组及其各自的概率,

    char[] chars = {'A', 'B', 'C', ...};
    double[] probabilities = {0.01, 0.02, 0.05, ...};
    

    然后您可以使用这样的循环自动执行所有if-ing:

    double r = Math.random();
    double cdf = 0.0;
    for (int i = 0; i < chars.length; i++) {
        cdf += probabilities[i]
        if (r < cdf) {
            return chars[i];
        }
    }
    return chars[chars.length - 1];
    

    在您的情况下,如果您的所有概率都是 1/96 的倍数,那么您可以选择小于 96 的随机整数而不是浮点数来做同样的事情。只需使用ints 代替doubles,并使用rnd.nextInt(96) 选择0 到95 之间的整数,包括0 和95,而不是Math.random()。此外,您的 probabilities 数组将包含实际概率乘以 96。

    char[] chars = {'A', 'B', 'C', ...};
    int[] probabilities = {5, 2, 4, ...}; // needs to sum to 96
    
    // later...
    
    int r = rnd.nextInt(96);
    int cdf = 0;
    for (int i = 0; i < chars.length; i++) {
        cdf += probabilities[i]
        if (r < cdf) {
            return chars[i];
        }
    }
    return chars[chars.length - 1];
    

    现在,如果您正在执行诸如从袋子中抽出拼字游戏瓷砖之类的事情,那么它会变得更加棘手,因为这是一个无需替换的抽样过程,即每次抽奖后概率都会发生变化。我认为在这种情况下更好的方法是实际使用一个集合来模拟袋子,然后为每个上面有该字母的图块添加一个字母的副本。您仍然可以使用与之前相同的 charsprobabilities 数组在循环中执行此操作:

    char[] chars = {'A', 'B', 'C', ...};
    int[] probabilities = {5, 2, 4, ...}; // number of tiles with each letter
    
    LinkedList<Character> bag = new LinkedList<Character>();
    for (int i = 0; i < chars.length; i++) {
        for (int n = 0; n < probabilities[i]; n++) {
            bag.add(chars[i]);
        }
    }
    

    然后你可以bag.shuffle() 来随机化图块,bag.pop() 让你随机选择一个。

    【讨论】:

      【解决方案3】:

      你可以这样做:

      List<char> letters = new List<char>();
      Dictionary<int,List<char>> set1 = new Dictionary<int,List<char>>();
      set1.Key = 2;
      set1.Value = new List<char>{'A','B'} //blah blah blah
      

      为这些字典创建一个数组或列表并foreach它们

      foreach (char theChar in set1.Value)
      {
        for (int i = 0; i < set1.Key;i++)
        {
          letters.add(theChar);
        }
      

      那么,

      Random random = new Random();
      char nextchar = letters[random.nextInt(letters.Count - 1)];
      

      您希望它被选中的次数越多,您将其添加到列表中的次数就越多。

      另外:如果需要,您可以用一种长度的字符串替换字符。

      编辑:这是添加字母的旧方法:

      for (int i = 0; i < 4; i++) // 4 times
      {
          letters.add('a');
      }
       for (int i = 0; i < 3; i++) // 4 times
      {
          letters.add('b');
      }
      

      等等

      【讨论】:

      • 我喜欢你的想法,但是如果我将字母分组到一个数组中,有没有办法做到这一点?像 char[] set1 = { J, K, Q, Y, Z } 然后给他们每个人一个 1/96 的权重?
      • @MattM - 试试你在我的回答中看到的 - 字典中的键代表权重,值代表具有该权重的字符
      【解决方案4】:

      Here's some documentation on generating random numbers in java.

      现在,假设您生成一个介于 0 和 95 之间的随机整数(96 种可能的变体)

      然后,您可以将每个字母映射到其中一个数字。一个简单而肮脏的方法是使用 switch 语句

      switch (randomNumber)
      {
          case 0:
              //decide that you want J
          break;
          case 1:
          case 2:
              // maybe you want a letter to have a 2/96 probability
          break;
      }
      

      另一种简单的方法是使用字符数组。

      Random rand = new Random(new Date().getTime())
      char[] charArray = {'A','B','C','C','D','E','F','F','F'};
      char chosenChar = charArray[rand.nextInt(0, 96)];
      

      【讨论】:

      • 我需要制作 96 个开关盒吗?有没有更清洁的方法?
      • @MattM 我刚刚预先编辑了我的帖子,建议制作一个字符数组。
      • @MattM 不管你做什么,你都必须做大量的陈述来给出每个字母的概率。
      • 你能给我一个我的程序使用哈希映射的例子吗?我们还没有学会它,但我们很快就会学会。
      • 我明白你做了什么!让我试一试!一会儿回来。
      【解决方案5】:
      Random r = new Random();
      char c = (char) r.nextInt(25)+65;
      

      http://www.asciitable.com/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-10
        • 1970-01-01
        • 1970-01-01
        • 2017-12-20
        • 2016-01-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多