简体   繁体   English

如何根据概率在Java中生成随机字母?

[英]How do I generate random letters in java based on probability?

I am having trouble generating random letters based on probability. 我无法根据概率生成随机字母。

For example, the letters J, K, Q, Y, Z each have a probability of 1/96 of occurring. 例如,字母J,K,Q,Y,Z的出现概率均为1/96。 A similar process (with higher probabilities) is used for other letters. 其他字母也使用类似的过程(概率更高)。

Can somebody show me how to do this? 有人可以告诉我该怎么做吗?

Edit to be specific: I'm writing a method called "getRandomLetter" that returns a char of a random letter based on a probability fraction. 进行具体编辑:我正在编写一种名为“ getRandomLetter”的方法,该方法基于概率分数返回随机字母的char。

The typical way to select from a discrete set of elements with specific probabilities is to choose a random floating-point number and find out which range it lies in. I'll explain with an example. 从具有特定概率的离散元素集中进行选择的典型方法是选择一个随机浮点数,并找出其位于哪个范围内。我将举例说明。 Suppose that you're choosing among three letters, A, B, and C, with probabilities 0.255, 0.407, and 0.338 respectively. 假设您在三个字母A,B和C中进行选择,概率分别为0.255、0.407和0.338。 You would compute a random number between 0 and 1 您将计算介于0和1之间的随机数

double r = Math.random();

and first compare it to the range from 0 to 0.255: 然后将其与0到0.255的范围进行比较:

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

then to the range from 0.255 to (0.255 + 0.407): 然后从0.255到(0.255 + 0.407)的范围:

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

and if it's not either of those, it has to be 'C' : 如果不是其中之一,则必须为'C'

else {
    return 'C';
}

If you're doing this with all 26 letters of the alphabet, it will be a pain to write out all 26 cases of the if - else statement. 如果使用字母表中的所有26个字母执行此操作,那么写出所有26种if - else语句的情况将很痛苦。 What you could do in advance is prepare an array of the characters and their respective probabilities, 您可以事先准备一个字符数组及其各自的概率,

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

and then you can automate all that if -ing with a loop like this: 然后你就可以自动所有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];

In your case, if all your probabilities are multiples of 1/96, then you can do the same thing choosing a random integer less than 96 instead of a floating-point number. 在您的情况下,如果所有概率都是1/96的倍数,那么可以选择小于96的随机整数而不是浮点数来做相同的事情。 Just use int s instead of double s, and use rnd.nextInt(96) to choose an integer between 0 and 95, inclusive, instead of Math.random() . 只需使用int而不是double ,然后使用rnd.nextInt(96)选择0到95(含rnd.nextInt(96)之间的整数,而不是Math.random() Also, your probabilities array would contain the actual probability times 96. 同样,您的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];

Now, if you're doing something like drawing Scrabble tiles out of a bag, then it becomes trickier because that is a sampling process without replacement, ie the probabilities change after every draw. 现在,如果您要进行一些操作,例如从袋子中画出Scrabble瓷砖,那将变得更加棘手,因为这是一个无需替换的采样过程,即,每次绘制后概率都会发生变化。 I think a better method in that case would be to actually use a collection to simulate the bag, and then actually add one copy of the letter for each tile that has that letter on it. 我认为在这种情况下,更好的方法是实际使用一个集合来模拟袋子,然后为上面带有字母的每个图块实际添加字母的一个副本。 You can still do this in a loop using the same chars and probabilities arrays from before: 您仍然可以使用之前相同的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]);
    }
}

Then you can bag.shuffle() to randomize the tiles, and bag.pop() lets you pick one at random. 然后,您可以bag.shuffle()随机化bag.pop()贴,而bag.pop()允许您随机选择一个。

Here's some documentation on generating random numbers in java. 这是一些在Java中生成随机数的文档。

Now, let's say you generate a random integer between 0 and 95 inclusive(96 possible variants) 现在,假设您生成一个介于0到95(含)之间的随机整数(96个可能的变体)

you can then map each of your letters to one of those numbers. 然后,您可以将每个字母映射到这些数字之一。 a simple and dirty way to do it would be a switch statement 一个简单而肮脏的方法是使用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;
}

another simple way to do it would be to use an array of chars. 另一个简单的方法是使用字符数组。

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)];

What you could do is something like this: 您可以做的是这样的:

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

make an array or list of these dictionaries and foreach them 制作这些词典的数组或列表,然后将它们

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

then, 然后,

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

the more times you want it to be likely to be picked, the more times you add it in the list. 您希望它被选择的次数越多,将其添加到列表中的次数就越多。

ALSO: you can replace chars with strings of one length if you want. 还可以:如果需要,可以将char替换为一个长度的字符串。

EDIT: here's the old way to add to letters: 编辑:这是添加字母的旧方法:

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

etc. 等等

Random r = new Random();
char c = (char) r.nextInt(25)+65;

http://www.asciitable.com/ http://www.asciitable.com/

The most eastetically pleasing solution would require a compact container for given letter's occurrence probabilities. 最令人愉悦的解决方案是为给定字母的出现概率要求使用紧凑的容器。 I'd suggest using a HashMap that would serve as a probability function (discrete distribution function). 我建议使用HashMap作为概率函数(离散分布函数)。 Like this: 像这样:

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

For sake it would be good to make sure, that overall sum of all probabilities is equal to 1.0 , but the numbers can be treated as probability weigths and normalized at the end. 为了方便起见,最好确保所有概率的总和等于1.0 ,但是这些数字可以视为概率权重,并在最后进行归一化。 You get the idea, right? 你明白了吧?

A purely mathematical aproach would require creating a cumulative distribution function, reverting it and then explicite using of that function. 纯粹的数学方法需要创建一个累积分布函数,将其还原,然后明确使用该函数。 That way you could provide a solution of generating any random values with pretty much any probability distribution. 这样,您可以提供一种生成具有几乎任何概率分布的随机值的解决方案。

Let's try doing it at once: 让我们尝试一下一次:

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
}

Now to use the map just call 现在要使用地图,只需调用

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM