WeBWorK Problems

Generate distinct random values

Generate distinct random values

by Dick Lane -
Number of replies: 2
I endorse Darwyn Cook's proposal (in the Features & Development forum) for a Tips & Tricks forum.  Here is my first contribution.


When I started writing PG problems, I would generate a pair of distinct integers with the pattern

$a = random( -9 , 9 , 1 ) ;
$b = random( -9 , 9 , 1 ) ;
while ($b == $a) {$b = random( -9 , 9 , 1 ) ;} ;

with the awkward extension

$a = random( -9 , 9 , 1 ) ;
$b = random( -9 , 9 , 1 ) ;
while ($b == $a) {$b = random( -9 , 9 , 1 ) ;} ;
$c = random( -9 , 9 , 1 ) ;
while (($c-$a)*($c-$b)==0) {$c = random( -9 , 9 , 1 ) ;} ;


That evolved into a more elegant/efficient form

$a = random( -9 , 9 , 1 ) ;
do {$b = random( -9 , 9 , 1 ) ;}  until ($b != $a) ;
do {$c = random( -9 , 9 , 1 ) ;} until ( ($c != $a) and ($c != $b) ) ;


An alternative using NchooseK is less transparent, but scales better for generating larger sets of distinct integers

loadMacros( “PGchoicemacros.pl” ) ;
@choose = NchooseK( 19 , 3 ) ;  ## values between 0 and (19-1)
$a = @choose[0] - 9 ;                ## indices between 0 and (3-1)
$b = @choose[1] - 9 ;
$c = @choose[2] - 9 ;


More recently, I generated 4 reals with distinct magnitudes (to be Real and Imaginary parts of 2 random complex numbers) using

$Rlo    = 0.5 ;
$Rhi    = 9.5 ;
$Rdelta = 0.5 ;
$a = random($Rlo,$Rhi,$Rdelta);
do {$b = random($Rlo,$Rhi,$Rdelta);} until ($b != $a) ;
do {$c = random($Rlo,$Rhi,$Rdelta);} until (($c != $a) and ($c != $b)) ;
do {$d = random($Rlo,$Rhi,$Rdelta);} until (($d!=$a) and ($d!=$b) and ($d!=$c));
$a = $a * list_random( -1 , 1 ) ;
$b = $b * list_random( -1 , 1 ) ;
$c = $c * list_random( -1 , 1 ) ;
$d = $d * list_random( -1 , 1 ) ;


I would like to learn about alternative (or simpler) techniques to do these (and similar) tasks.
In reply to Dick Lane

Re: Generate distinct random values

by Gavin LaRose -
Hi Dick,

Depending on the ranges over which the variables should extend and the number of variables being generated, using non_zero_random is also possible:

$a = random(1,5,1);
$b = $a + non_zero_random(-3,3,1);

Though I ran into an interesting difficulty with non_zero_random recently: I had something like

$x0 = random(1,5,1);
$dx = non_zero_random(-0.3,0.3,0.1);
$x1 = $x0 + $dx;

and for a specific seed, $x1 came out to be a number on the order of 5.55x10^(-17). My guess is that this is a numerical precision error when storing the decimal values 0.3 and 0.1 in binary (as the computer must). I haven't looked at the code for non_zero_random, but my guess is that it's adding a multiple of the increment to the start value, avoiding zero. In this case I'm guessing that the conversion of the decimals to binary resulted in the sum having the value 5.55x10^(-17), which is (technically) non-zero. My work around was to take

$x0 = random(1,5,1);
$dx = list_random(-0.3,-0.2,-0.1,0.1,0.2,0.3);
$x1 = $x0 + $dx;

which resolved the problem.

FWIW, in any event,
Gavin
In reply to Gavin LaRose

Re: Generate distinct random values

by Nathan Wodarz -
My solution when I had the same problem that Gavin saw was to do the following:

$dx = non_zero_random(-3,3)/10;

If the random or non_zero_random function can give an integer, I have never seen issues with dividing by 10 at that point. list_random is certainly a reasonable alternative in this case, but I have had problems where I've wanted $dx to be a non-zero integer multiple of 0.001 between -1 and 1. In this case

$dx = non_zero_random(-999,999)/1000;

works perfectly.