tags:

views:

121

answers:

3
+1  Q: 

PHP Random numbers

Hello,

I would like to draw a random number from the interval 1,49 but I would like to add a number as an exception ( let's say 44 ) , I cannot use round(rand(1,49)) .So I decided to make an array of 49 numbers ( 1-49) , unset[$aray[44]] and apply array_rand

Now I want to draw a number from the interval [$left,49] , how can I do that using the same array that I used before ?The array now misses value 44.

A: 

Why not just check your exceptions:

function getRand($min, $max) {
    $exceptions = array(44, 23);
    do {
        $rand = mt_rand($min, $max);
    } while (in_array($rand, $exceptions));
    return $rand;
}

Note that this could result in an infinite loop if you provide a min and max that force mt_rand to return an exception character. So if you call getRand(44,44);, while meaningless, will result in an infinite loop... (And you can avoid the infinite loop with a bit of logic in the function (checking that there is at least one non-exception value in the range $min to $max)...

The other option, would be to build the array with a loop:

function getRand($min, $max) {
    $actualMin = min($min, $max);
    $actualMax = max($min, $max);
    $values = array();
    $exceptions = array(44, 23);
    for ($i = $actualMin; $i <= $actualMax; $i++) {
        if (in_array($i, $exceptions)) {
            continue;
        }
        $values[] = $i;
    }
    return $values[array_rand($values)];
}
ircmaxell
if ($min==$max) return $min; ---- this can be added as the 1st line .I was looking for the most efficient method . I was thinking of making an array_diff($originalarray,$exception) but talking on number of steps I think that it takes longer than your function
nevergone
Sure, you can add some very simple logic to prevent the loops. I just wanted to demonstrate the technique. Not necessarily provide production level code (After all, I'm not here to do the work for you, only show you the path)...
ircmaxell
+2  A: 

The function pick takes an array as an argument with all the numbers you have already picked. It will then pick a number between the start and the end that IS NOT in that array. It will add this number into that array and return the number.

function pick(&$picked, $start, $end) {

    sort($picked);

    if($start > $end - count($picked)) {
        return false;
    }

    $pick = rand($start, $end - count($picked));
    foreach($picked as $p) {
        if($pick >= $p) {
            $pick += 1;
        } else {
            break;
        }
    }

    $picked[] = $pick;

    return $pick;
}

This function will efficiently get a random number that is not in the array AND WILL NEVER INFINITELY RECURSE!

To use this like you want:

$array = array(44); // you have picked 44 for example
$num = pick($array, 1, 49); // get a random number between 1 and 49 that is not in $array  

// $num will be a number between 1 and 49 that is not in $arrays

How the function works


Say you are getting a number between 1 and 10. And you have picked two numbers (e.g. 2 and 6). This will pick a number between 1 and (10 minus 2) using rand: rand(1, 8).

It will then go through each number that has been picked and check if the number is bigger.

For Example:

If rand(1, 8) returns 2. 
  It looks at 2 (it is >= then 2 so it increments and becomes 3)
  It looks at 6 (it is not >= then 6 so it exits the loop)
  The result is: 3

If rand(1, 8) returns 3
  It looks at 2 (it is >= then 2 so it increments and becomes 4)
  It looks at 6 (it is not >= then 6 so it exits the loop)
  The result is 4

If rand(1, 8) returns 6
  It looks at 2 (it is >= then 2 so it increments and becomes 7)
  It looks at 6 (it is >= then 6 so it increments and becomes 8)
  The result is: 8

If rand(1, 8) returns 8
  It looks at 2 (it is >= then 2 so it increments and becomes 9)
  It looks at 6 (it is >= then 6 so it increments and becomes 10)
  The result is: 10

Therefore a random number between 1 and 10 is returned and it will not be 2 or 6.

I implemented this a long time ago to randomly place mines in a 2-dimensional array (because I wanted random mines, but I wanted to guarantee the number of mines on the field to be a certain number)

Bob Fincheimer
Good idea .Thank you
nevergone
Accept if it worked... if it didn't tell me what error(s) you are getting and I will fix it
Bob Fincheimer
A: 

The simplest solution would be to just search for a random number from min to max - number of exceptions. Then just add 1 to the result for each exception lower than the result.

function getRandom($min, $max)
{
  $exceptions = array(23, 44); // Keep them sorted or you have to do sort() every time
  $random = rand($min, $max - count($exceptions));
  foreach ($exceptions as $ex)
  {
    if ($ex > $random) break;
    ++$random;
  }
  return $random;
}

Runtime should be O(1+n) with n being the number of exceptions lower than the result.

dbemerlin