Выбрать случайный индекс массива с условием на значение


У меня есть массив булевых значений, из которого я хочу выбрать случайный индекс, значение которого равно true, и установить его в false.

Я могу, конечно, сделать это с помощью грубой силы, выбирая индексы, пока не попаду в тот, значение которого истинно:

$arr = array(true, false, false, true, false, true);

var_dump($arr);

$i = array_rand($arr);
while(!$arr[$i])
{
    $i = array_rand($arr);
}
$arr[$i] = false;

var_dump($arr);

Это создает что-то вроде этого, где четвертая запись была изменена.

array(6) {
  [0]=>
  bool(true)
  [1]=>
  bool(false)
  [2]=>
  bool(false)
  [3]=>
  bool(true)
  [4]=>
  bool(false)
  [5]=>
  bool(true)
}

array(6) {
  [0]=>
  bool(true)
  [1]=>
  bool(false)
  [2]=>
  bool(false)
  [3]=>
  bool(false)
  [4]=>
  bool(false)
  [5]=>
  bool(true)
}
Однако мне приходится проделывать эту операцию несколько раз со значительно большим массивом. В какой-то момент массив почти полностью ложен, и в этом случае зверь силовой метод довольно неэффективен. Есть ли более изящный метод решения этой проблемы? Любой вид array_rand() функции, где я могу дать предварительное условие?
3 3

3 ответа:

$arr = array(true,true,false,false,true,false);

$res = array_keys($arr, true);

var_dump($res);  // returns 0,1,4

echo $res[array_rand($res)]; //echo one of the indexes that is true

Приведенный выше код возвращает индексы истинных значений $arr в $res.

Https://3v4l.org/CG1v2

Правка. Чтобы затем установить один из индексов $arr как false, вы должны:

$arr[$res[array_rand($res)]] = false; // will set one as false.

Зацикливание этих двух строк в конечном итоге установит все индексы в false:

$res = array_keys($arr, true);
$arr[$res[array_rand($res)]] = false;

Вы можете использовать следующий код:

$arr = array(true, false, false, true, false, true);

$randTrueIndex = array_rand(array_filter($arr, function($item) { 
    return $item;
}));

$arr[$randTrueIndex] = false;

Самым простым способом сделать это, не тратя впустую никаких усилий, было бы создать случайную перестановку индексов массива. Перемешивание кнута (также известное какFisher-Yates shuffle ) должно работать превосходно.

Другим вариантом для некоторых приложений будет выбор генератора, который создает значения в желаемом диапазоне без повторения или только с относительно небольшим числом выбросов (значения, которые выходят за пределы целевого диапазона). Например, все линейно-конгруэнтные генераторы обладают тем свойством, что любые младшие N битов имеют цикл с периодом 2^n. выберите первую степень из двух, которая не меньше размера вашего массива, и вы будете генерировать меньше одного потерянного числа для каждого хорошего в среднем.