Sebastian
Sebastian

Reputation: 805

Choose random index of array with condition on value

I have an array of booleans, from which I want to pick a random index whose value is true and set it to false.

I can, of course, do this with brute force by picking indices until I hit one whose value is true:

$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);

This creates something like this, where the fourth entry got changed.

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)
}

However, I have to do this operation several times with a significantly larger array. At some point the array is nearly completely false, in which case the brute force method is rather inefficient.

Is there any more elegant method of solving this problem? Any kind of array_rand() function, where I can give a precondition?

Upvotes: 3

Views: 1303

Answers (3)

Andreas
Andreas

Reputation: 23958

$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

The above code returns the indexes of the true values of $arr in $res.

https://3v4l.org/CG1v2

Edit. To then set one of $arr indexes as false you should:

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

Looping these two lines will eventually set all indexes to false:

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

Upvotes: 2

DarthGizka
DarthGizka

Reputation: 4665

The simplest way to do this without wasting any work would be to create a random permutation of the array indices. The Knuth shuffle (also known as Fisher-Yates shuffle) should work admirably.

Another option for certain applications would be picking a generator that creates values in the desired range without repetition, or with only a comparatively small number of outliers (values that fall outside the target range). For example, all linear-congruential generators have the property that any lower n bits cycle with period 2^n. Pick the first power of two that is not less than your array size and you'll generate less than one wasted number for every good one on average.

Upvotes: 0

Ismail RBOUH
Ismail RBOUH

Reputation: 10450

You can use the following code:

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

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

$arr[$randTrueIndex] = false;

Upvotes: 1

Related Questions