В качестве забавного проекта я занимаюсь разработкой игры-симулятора крикета, и одним из главных ее аспектов является получение случайного результата при каждой игре в шары.

В общем, в Test Cricket такая возможность:

«0», «1» встречаются очень часто (60% времени)

«2», «3» происходят умеренно (25% времени)

«ЧЕТЫРЕ», «ШЕСТЬ», «ВНЕ» случаются редко (в 10% случаев)

«ШИРОКИЙ ШАР», «НЕТ ШАР» случаются очень редко (2% времени)

Если у меня есть массив, такой как:

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

Что может быть лучшим способом получить вышеупомянутую вероятность при извлечении случайного элемента из possible_outcomes за фиксированное число итераций, скажем, 60.

PS: извините, если некоторые из вас не знают о спорте. Я использовал пару терминов, связанных с крикетом, так как я не знал, как объяснить лучше.

4
asprin 13 Дек 2016 в 14:00

6 ответов

Лучший ответ

Я предлагаю использовать непрерывную проверку вероятности и остатка случайного числа.

Эта функция устанавливает сначала возвращаемое значение в последний возможный индекс и выполняет итерацию до тех пор, пока остаток случайного значения не станет меньше реальной вероятности.

Вероятности должны быть равны единице.

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; }
1
Nina Scholz 13 Дек 2016 в 11:43

Техника, которую вы хотите использовать, называется «взвешенная случайность». Для достижения этого в Javascript вы можете создать массив, который будет содержать возможные результаты в количествах, которые соответствуют заданной вероятности их появления. Подробнее о том, как это сделать и почему это работает, см. этот вопрос.

В логике вашей игры в крикет вы можете использовать цикл для извлечения случайных результатов до тех пор, пока не закончится тест, что-то вроде этого:

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>

Обратите внимание, что вероятности не будут полностью соответствовать тому, что вы указали в приведенном выше коде, поскольку они не накапливаются в сумме до 1,00, но значения можно легко изменить.

1
Community 23 Май 2017 в 12:08

Вы можете сгенерировать случайное число от 0 до 100. Если 0 <число <10, то 0 прогон, если 20-25, то 2 прогон. Точно так же, если мы получим 100, то калитка.

1
Amit 13 Дек 2016 в 11:06

Легко изменяемое решение будет следующим:

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)];
1
Bálint 13 Дек 2016 в 11:24

Простите за мой неуклюжий код, но я надеюсь, что вы можете рассмотреть это как решение? : 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>
1
philantrovert 13 Дек 2016 в 11:27

ИМО самое простое, но не очень удачное решение - создать массив из сотен элементов с повторяющимися элементами:

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

Тогда вы просто получаете случайный элемент из этого массива. Нет, если или переключатели необходимы.

PS . Я не знаю крикета, однако, если повторение 100 раз «0» не разрешено, вы всегда можете удалить выбранную опцию из массива, чтобы это больше не повторилось.

1
piotrwest 13 Дек 2016 в 11:12