简体   繁体   中英

Hi I want to use math.random to set the likely for it to repeat

I want to make a program that will generate a random number. I know I need to use Math.random() for this but I want to set a ratio for a number that can repeat. for example, I want the compiler to give a number from 1-10 but I want 5 repeat 3 times more than another number. how do I do that? please help thank you.

for exactly that case:

private static final int[] CHOICES = new int[] { 1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9, 10 };
public static int strangeRandom() {
    return CHOICES[ThreadLocalRandom.current().nextInt(CHOICES.length)];
}

selects randomly from a given set of choices & you control the choices so that 5 is more likely.

This will have the 5 come up at least 3 times as often as the other 9 numbers:

private static int getRandom ()
{
    Random r = new Random();

    int next = r.nextInt(100000) + 1;

    if (next <= 25000)
    {
        return 5;
    }
    else
    {
        return r.nextInt(10) + 1;
    }
}

Note : You can also preclude the 5 in the else case to try and hit the "3 times" likelihood better. I didn't for simplicity.

Another Variation : Here is a variation that uses modulus of the nextInt() instead of 25000/100000 split. Also, I put code in the else case that tries to preclude the 5 (without the infinite loop).

private static int getRandom ()
{
    Random r = new Random();

    int next = r.nextInt();

    if (next % 4 == 0)
    {
        return 5;
    }
    else
    {
        int p = r.nextInt(10) + 1;
        int tries = 0;

        // try to not get a 5 for a few times
        while (p == 5 && tries++ < 4)
        {
            p = r.nextInt(10) + 1;
        }
        return p;
    }
}

Test Code :

public static void main(String[] args)
{       
    Map<Integer, Integer> frequecnyMap = new HashMap<Integer, Integer>();

    for (int i = 0; i < 12000; i++)
    {
        int r = getRandom();

        Integer n = frequecnyMap.get(r);

        if (n == null)
        {
            frequecnyMap.put(r, 1);
        }
        else
        {
            frequecnyMap.put(r, n + 1);
        }
    }
    System.out.println(frequecnyMap);
}

Sample Output (2nd Variation) :

{1=971, 2=975, 3=995, 4=1037, 5=3025, 6=1042, 7=995, 8=976, 9=969, 10=1015}

{1=1016, 2=1019, 3=994, 4=968, 5=3068, 6=1030, 7=996, 8=914, 9=990, 10=1005}

{1=939, 2=944, 3=979, 4=986, 5=3023, 6=1040, 7=1007, 8=1046, 9=997, 10=1039}

First, you should use the Random class, not Math.random() . For one, it has the nice helper method nextInt(int n) that generates a random integer between 0 and n-1 (inclusive).

In your particular case, you want a number 1-10, so generate 0-9 and add 1, ie nextInt(10) + 1 .

But you want the number 5 to occur 3 times more often. A quick dirty way is the generate two extra number (1-12) and map them to 5.

Random rnd = new Random();
int num = rnd.nextInt(12) + 1;
if (num == 11 || num == 12)
    num = 5;

As I said, quick and dirty, but it does the trick for your particular case.


Now, for a more generic solution, you want to be able to specify weighted probabilities. Numbers 1-4, 6-10 has a weight of 1, and 5 has a weight of 3.

What you then do is sum the weight (12), and generate a random number, then find the number where the accumulated weight exceeds the random number.

Here is a method for that:

private static int random(Random rnd, int ... weights) {
    if (weights.length < 2)
        throw new IllegalArgumentException("Need at least two weights");
    int total = 0;
    for (int weight : weights) {
        if (weight <= 0)
            throw new IllegalArgumentException("Invalid weight: " + weight);
        if ((total += weight) < 0)
            throw new IllegalArgumentException("Weight overflow");
    }
    for (int i = 0, val = rnd.nextInt(total); ; i++, val -= weights[i])
        if (val < weights[i])
            return i;
}

You could then call it like this:

Random rnd = new Random();
int num = random(rnd, 1,1,1,1,3,1,1,1,1,1) + 1;

Notice the +1 , since the method is like Random.nextInt(n) and returns a number 0 to n-1, where n is the number of weights given.

You could easily turn it into a nice class, where the weights are given on the constructor, and the class manages the Random object for you.

Beware that performance will degrade as number of weights go up. There is a way to use TreeMap or binarySearch to improve that, but you need the class implementation, so it can prepare the data.

If you want to have full control of your random generated numbers, you should do something like this:

public class MyRandom {
    private int[] probability;
    private long[] ntimes;
    private long times;

    public MyRandom(int[] probability) {
        this.probability = new int[10];
        System.arraycopy(probability, 0, this.probability, 0, probability.length);
        ntimes=new long[10];
        for(int i=0; i < ntimes.length; i++)
            ntimes[i]=0;
        times=0;
    }

    public void showProbability() {
        for (long i : probability) {
            System.out.print(i+" ");
        }
        System.out.println();
    }

    public int random() {
        int t = 10;

        int r = (int)Math.floor(Math.random()*10+1);
        double p = 0;
        if (times == 0) 
            p = 0;
        else
            p = ntimes[r-1]*100/times;

        System.out.println("P: "+p +" : "+probability[r-1]);
        while (p > probability[r-1] && t > 0) {
            r = (int)Math.floor(Math.random()*10+1);
            p = ntimes[r-1]*100/times;            
            t--;
        }
        ntimes[r-1]++;
        times++;
        return r;
    }

    public long getOcurrences(int i) {
        return ntimes[i-1];
    }


    //This is an example of how to use it.        
    public static void main(String[] args) {
        int[] p = {5, 5, 5, 5, 30, 5, 5, 5, 10, 15};
        MyRandom mr = new MyRandom(p);

        for (int i = 0; i < 2000; i++) {
            int r = mr.random();
            System.out.println("Id: "+i+" Number: "+r+" Ocurrences: "+mr.getOcurrences(r));
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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