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