views:

219

answers:

4

Using PHP (other languages, using common built-ins are welcome), how can I get a random (or pseudo-random) number that does NOT match a certain criteria?

IE: I want $x = rand(0, 99) but only if ($x % 9) != 0.

What is a clean acceptable way to constrain random numbers to a criteria like that?

As an example, using a while() loop, and possibly breaking when we have something that meets our criteria, then using the random number after the loop?:

while ( ($rand_key = array_rand($array_of_items)) && ($rand_key % 9 == 0) ) 
{ 
    // Do nothing?
}

Or something like:

while ( $rand_key = array_rand($array_of_items) ) 
{ 
    if ( $rand_key % 9 == 0 ) {
        break;
    }
}

Or is there a more concise or appropriate way to accomplish this?

+4  A: 

Is that your exact usage case, or just an example?

If you're trying (in particular) to get numbers from 0 to 99 excluding exact multiples of 9, I can think of a couple of ways to avoid the awkward looping. People don't like to loop while generating random numbers because you could loop forever. (But of course that won't happen unless you're the extremely unlucky type--the sort of person who happens to get himself in gunfights with Clint Eastwood in spaghetti westerns.)

  1. You could have a lookup table that remaps the random numbers you get into the ones you want.

  2. Generate 0-88 randomly. Multiply the result by 9 and divide it by 8 and get the integer result (floor, not round). Then add 1.

Both of these have predictable timing.

Nosredna
if ($rand%9==0) $rand++; // :-)
Csaba Kétszeri
@Csaba--I've done that a few times. But it works only if you don't care about the distribution of numbers.
Nosredna
+3  A: 

One of the rare cases where a do-while loop actually helps

$testData = array( 7, 8, 9 );

do {
  $rand = array_rand( $testData );
} while ( $testData[$rand] % 9 == 0 );

echo $testData[$rand];
Peter Bailey
+1 — A do-while is ideal for this type of scenario. (Attempt, check, attempt again on failure.)
Ben Blank
Do...While hears his name and his ears perk up. Me!? Someone wants to use me!
T Pops
A: 

This matches the 'concise' requirement:

while( ($x = rand(0, 99)) % 9 == 0 );
echo $x;

or

while ( $array_of_items[$rand_key = array_rand($array_of_items)] % 9 == 0 ); 
echo $array_of_items[$rand_key];

However, it isn't very readable.

Tom Haigh
+1  A: 

An alternative method, that trades space for, potentially, multiple PRNG calls, for cases with small numbers of valid results (as in your example) would be to populate an array with the valid solutions and use the PRNG to produce an index into the array.

Whatever method you choose I'd also suggest, for production code, noting the reason for the restriction in an appropriate and accessible way.

mas
I absolutely agree with noting any strange reasoning in Documentation. Unless you were referring to my code examples, which were only examples. Thanks for your answer.
anonymous coward