简体   繁体   中英

How to get a balanced output when picking a random element from an array?

As a fun project, I'm developing a cricket simulator game and one of the main aspects of it is to have a random outcome on each delivery bowled.

In general, in Test Cricket, the possibility is as follows:

"0", "1" happen very frequently (60% of the time)

"2", "3" happen moderately (25% of the the time)

"FOUR", "SIX", "OUT" happen rarely (10% of the time)

"WIDE BALL", "NO BALL" happen very rarely (2% of the time)

If I have an array such as:

var possible_outcomes = ["0","1","2","3","FOUR","SIX","OUT","WIDE BALL","NO BALL"];

What could be the best way to get the above mentioned probability when pulling a random item from possible_outcomes over a fixed number of iterations, say 60.

PS: I'm sorry if some of you are unaware of the sport. I've used a couple of terms related to Cricket since I didn't know how to explain any better.

You can generate a random number between 0-100. If 0 < number < 10 then 0 run, if 20-25 then 2 run. Similarly if we get 100 then wicket.

IMO the easiest, however not so fancy solution is to create an array of hundred elements with repeated items in it:

var possible_outcomes = ["0","0","0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0","0","0",
"0","0","0","0","0","0","0","0","0","0",
"1","1","1","1","1","1","1","1","1","1",
"1","1","1","1","1","1","1","1","1","1",
"1","1","1","1","1","1","1","1","1","1",
,and so on...,
,"WIDE BALL","WIDE BALL","NO BALL","NO BALL"];

Then you just get the random item from that array. No if's or switch-cases needed.

PS. I don't know the cricket, however if repeating 100 times "0" is not allowed, you can always remove selected option from the array, so it won't happen again.

I suggest to use a continuous check of the probability and the rest of the random number.

This function sets first the return value to the last possible index and iterates until the rest of the random value is smaller than the actual probability.

The probabilities have to sum to one.

 function getRandomIndexByProbability(probabilities) { var r = Math.random(), index = probabilities.length - 1; probabilities.some(function (probability, i) { if (r < probability) { index = i; return true; } r -= probability; }); return index; } var i, action = ["0", "1", "2", "3", "FOUR", "SIX", "OUT", "WIDE BALL", "NO BALL", "other"], probabilities = [0.3, 0.3, 1 / 8, 1 / 8, 1 / 30, 1 / 30, 1 / 30, 0.01, 0.01, 0.03], count = {}, index; action.forEach(function (a) { count[a] = 0; }); for (i = 0; i < 1e6; i++) { index = getRandomIndexByProbability(probabilities); count[action[index]]++; } console.log(count); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

The technique you want to utilise is called 'weighted randomness'. To achieve this in Javascript you can build an array which is populated with the possible outcomes in amounts which match the given probability of their occurrence. For details on how to do this and why it works, see this question .

In the logic of your cricket game you can then use a loop to pull out random outcomes until the batter is out, something like this:

 function weightedRand(spec) { var i, j, table = []; for (i in spec) { for (j = 0; j < spec[i] * 10; j++) { table.push(i); } } return function() { return table[Math.floor(Math.random() * table.length)]; } } var outcomes = weightedRand({ '0': 0.3, '1': 0.3, '2': 0.125, '3': 0.125, 'FOUR': 0.033, 'SIX': 0.033, 'OUT': 0.033, 'WIDE BALL': 0.01, 'NO BALL': 0.01 }); $('button').click(function() { clearInterval(interval); $('.innings').empty(); $(this).prop('disabled', true); var interval = setInterval(function() { var item = outcomes(); $('.innings').append('<div>' + item + '<div>'); if (item == 'OUT') { clearInterval(interval); $('button').prop('disabled', false); } }, 1000); }) 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <button>Start innings</button> <div class="innings"></div> 

Note that the probabilities won't quite match what you've specified in the above code as they don't cumulatively add up to 1.00, but the values can easily be amended.

An easiely changeable solution would be the following:

var cricket = [
    {
        name: 0,
        weight: 30
    },
    {
        name: 1,
        weight: 30
    },
    {
        name: 2,
        weight: 13
    }
    // ...
];

// Create the chances array by putting as many instances of a value in it, as it's weight

var chances = [];
for (var i = 0; i < cricket.length; i++) {
    for (var j = 0; j < cricket[i].weight; j++) {
        chances.push(cricket[i].name);
    }
}

// getting a value

var value = chances[Math.floor(Math.random() * chances.length)];

Forgive me for my awkward code but I hope you can consider this as a solution? :D

 function repeatArray(value, len) { if (len == 0) return []; var a = [value]; while (a.length * 2 <= len) a = a.concat(a); if (a.length < len) a = a.concat(a.slice(0, len - a.length)); return a; } var zeroes = repeatArray("0", 30); var ones = repeatArray("1", 25); var twos = repeatArray("2", 15); var threes = repeatArray("3", 10); var fours = repeatArray("FOUR", 5); var sixes = repeatArray("SIX", 5); var wickets = repeatArray("OUT", 5); var extras = repeatArray("Extra", 5); var finalArr = []; finalArr = finalArr.concat(zeroes, ones, twos, threes, fours, sixes, wickets, extras); for (var i = 0; i < 20; i++) { var idx = Math.floor(Math.random() * finalArr.length); $("#out").append(finalArr[idx]+", "); } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="out"></div> 

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