1#!./perl 2 3# From Tom Phoenix <rootbeer@teleport.com> 22 Feb 1997 4# Based upon a test script by kgb@ast.cam.ac.uk (Karl Glazebrook) 5 6# Looking for the hints? You're in the right place. 7# The hints are near each test, so search for "TEST #", where 8# the pound sign is replaced by the number of the test. 9 10# I'd like to include some more robust tests, but anything 11# too subtle to be detected here would require a time-consuming 12# test. Also, of course, we're here to detect only flaws in Perl; 13# if there are flaws in the underlying system rand, that's not 14# our responsibility. But if you want better tests, see 15# The Art of Computer Programming, Donald E. Knuth, volume 2, 16# chapter 3. ISBN 0-201-03822-6 (v. 2) 17 18BEGIN { 19 chdir "t" if -d "t"; 20 @INC = qw(. ../lib); 21} 22 23use strict; 24use Config; 25 26require "./test.pl"; 27 28 29my $reps = 100_000; # How many times to try rand each time. 30 # May be changed, but should be over 500. 31 # The more the better! (But slower.) 32 33my $bits = 8; # how many significant bits we check on each random number 34my $nslots = (1<< $bits); # how many different numbers 35 36plan(tests => 7 + $nslots); 37 38# First, let's see whether randbits is set right and that rand() returns 39# an even distribution of values 40{ 41 my $sum; 42 my @slots = (0) x $nslots; 43 my $prob = 1/$nslots; # probability of a particular slot being 44 # on a particular iteration 45 46 # We are going to generate $reps random numbers, each in the range 47 # 0..$nslots-1. They should be evenly distributed. We use @slots to 48 # count the number of occurrences of each number. For each count, we 49 # check that it is in the range we expect. For example for reps = 50 # 100_000 and using 8 bits, we expect each count to be around 51 # 100_000/256 = 390. How much around it we tolerate depends on the 52 # standard deviation, and how many deviations we allow. If we allow 53 # 6-sigmas, then that means that in only 1 run in 506e6 will be get a 54 # failure by chance, assuming a fair random number generator. Given 55 # that we test each slot, the overall chance of a false negative in 56 # this test script is about 1 in 2e6, assuming 256 slots. 57 # 58 # the actual count in a slot should follow a binomial distribution 59 # (e.g. rolling 18 dice, we 'expect' to see 3 sixes, but there's 60 # actually a 24% chance of 3, a 20% change of 2 or 4, a 12% 61 # chance of 1 or 5, and a 4% chance of 0 or 6 of them). 62 # 63 # This makes it easy to calculate the expected mean a standard 64 # deviation; see 65 # https://en.wikipedia.org/wiki/Binomial_distribution#Variance 66 67 my $mean = $reps * $prob; 68 my $stddev = sqrt($reps * $prob * (1 - $prob)); 69 my $sigma6 = $stddev * 6.0; # very unlikely to be outside that range 70 my $min = $mean - $sigma6; 71 my $max = $mean + $sigma6; 72 73 note("reps=$reps; slots=$nslots; min=$min mean=$mean max=$max"); 74 75 for (1..$reps) { 76 my $n = rand(1); 77 if ($n < 0.0 or $n >= 1.0) { 78 diag(<<EOM); 79WHOA THERE! \$Config{drand01} is set to '$Config{drand01}', 80but that apparently produces values ($n) < 0.0 or >= 1.0. 81Make sure \$Config{drand01} is a valid expression in the 82C-language, and produces values in the range [0.0,1.0). 83 84I give up. 85EOM 86 exit; 87 } 88 $slots[int($n * $nslots)]++; 89 } 90 91 for my $i (0..$nslots - 1) { 92 # this test should randomly fail very rarely. If it fails 93 # for you, try re-running this test script a few more times; 94 # if it goes away, it was likely a random (ha ha!) glitch. 95 # If you keep seeing failures, it means your random number 96 # generator is producing a very uneven spread of values. 97 ok($slots[$i] >= $min && $slots[$i] <= $max, "checking slot $i") 98 or diag("slot $i; count $slots[$i] outside expected range $min..$max"); 99 } 100} 101 102 103# Now, let's see whether rand accepts its argument 104{ 105 my($max, $min); 106 $max = $min = rand(100); 107 for (1..$reps) { 108 my $n = rand(100); 109 $max = $n if $n > $max; 110 $min = $n if $n < $min; 111 } 112 113 # This test checks to see that rand(100) really falls 114 # within the range 0 - 100, and that the numbers produced 115 # have a reasonably-large range among them. 116 # 117 cmp_ok($min, '>=', 0, "rand(100) >= 0"); 118 cmp_ok($max, '<', 100, "rand(100) < 100"); 119 cmp_ok($max - $min, '>=', 65, "rand(100) in 65 range"); 120 121 122 # This test checks that rand without an argument 123 # is equivalent to rand(1). 124 # 125 $_ = 12345; # Just for fun. 126 srand 12345; 127 my $r = rand; 128 srand 12345; 129 is(rand(1), $r, 'rand() without args is rand(1)'); 130 131 132 # This checks that rand without an argument is not 133 # rand($_). (In case somebody got overzealous.) 134 # 135 cmp_ok($r, '<', 1, 'rand() without args is under 1'); 136} 137 138{ # [perl #115928] use a standard rand() implementation 139 srand(1); 140 is(int rand(1000), 41, "our own implementation behaves consistently"); 141 is(int rand(1000), 454, "and still consistently"); 142} 143