How do I generate random letters in java based on

2020-05-06 04:56发布

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. 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.

5条回答
做个烂人
2楼-- · 2020-05-06 05:21

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). 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. 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());
查看更多
Juvenile、少年°
3楼-- · 2020-05-06 05:29

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. You would compute a random number between 0 and 1

double r = Math.random();

and first compare it to the range from 0 to 0.255:

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

then to the range from 0.255 to (0.255 + 0.407):

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

and if it's not either of those, it has to be '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. 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:

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. Just use ints instead of doubles, and use rnd.nextInt(96) to choose an integer between 0 and 95, inclusive, instead of Math.random(). Also, your probabilities array would contain the actual probability times 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, i.e. the probabilities change after every draw. 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:

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.

查看更多
贼婆χ
4楼-- · 2020-05-06 05:34

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

Now, let's say you generate a random integer between 0 and 95 inclusive(96 possible variants)

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 (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)];
查看更多
疯言疯语
5楼-- · 2020-05-06 05:37
Random r = new Random();
char c = (char) r.nextInt(25)+65;

http://www.asciitable.com/

查看更多
神经病院院长
6楼-- · 2020-05-06 05:37

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.

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.

查看更多
登录 后发表回答