简体   繁体   English

从数组中选择随机元素时如何获得平衡输出?

[英]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: 一般来说,在Test Cricket中,可能性如下:

"0", "1" happen very frequently (60% of the time) “0”,“1”经常发生(60%的时间)

"2", "3" happen moderately (25% of the the time) “2”,“3”适度发生(25%的时间)

"FOUR", "SIX", "OUT" happen rarely (10% of the time) “四”,“六”,“出”很少发生(10%的时间)

"WIDE BALL", "NO BALL" happen very rarely (2% of the time) “WALL BALL”,“NO BALL”很少发生(2%的时间)

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. 有什么能拉从一个随机的项目时,得到上述概率的最好方法possible_outcomes在固定的迭代次数,说60。

PS: I'm sorry if some of you are unaware of the sport. PS:如果你们中的一些人不知道这项运动,我很抱歉。 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. 您可以生成0到100之间的随机数。 If 0 < number < 10 then 0 run, if 20-25 then 2 run. 如果0 <number <10则运行0,如果20-25则运行2。 Similarly if we get 100 then wicket. 同样,如果我们获得100然后检票口。

IMO the easiest, however not so fancy solution is to create an array of hundred elements with repeated items in it: IMO最简单但不那么花哨的解决方案是创建一个包含重复项目的数百个元素的数组:

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. 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. 我不知道板球,但是如果不允许重复100次“0”,你总是可以从阵列中删除所选的选项,所以它不会再发生。

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. 概率必须总和为1。

 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. 要在Javascript中实现此目的,您可以构建一个数组,该数组中填充的数量可能与给定的发生概率相匹配。 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. 请注意,概率与上面代码中指定的概率不完全匹配,因为它们不累加到1.00,但可以很容易地修改这些值。

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

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

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