http://www.perlmonks.org?node_id=1054438

vsespb has asked for the wisdom of the Perl Monks concerning the following question:

I need to get predictible integer random number sequence (for use in unit tests).
Sequence should be strictly same on each run. It does not have to be really random (nor secure random of course)

Seems it's docummented that I can use srand() for this:
perldoc srand:
However, there are a few situations where programs are likely to want to call srand. One is for generating predictable results, generally for testing or debugging. There, you use srand($seed), with the same $seed each time.
So I wrote the following code:
use strict; use warnings; use List::Util qw/min/; sub t { sprintf("%0.0f", shift()*10000) } sub rn { my $e = 1e-6; my $x = rand(); return min(t($x), t($x+$e), t($x-$e)); } srand(42654); print rn(), "\n"; print rn(), "\n"; print rn(), "\n"; print rn(), "\n";
prints:
3725 3516 7534 1948
I use
min(t($x), t($x+$e), t($x-$e))
to get rid of possible float off-by-0.0000...00001 problems.
Questions are:
1. Will this work as expected? On each and every possible perl build and platform?
2. Is there a better way to do it?

Replies are listed 'Best First'.
Re: Predictable random sequence
by hdb (Monsignor) on Sep 17, 2013 at 14:14 UTC
      Yes, this would work, example implementation: http://rosettacode.org/wiki/Linear_congruential_generator#Perl
Re: Predictable random sequence
by keszler (Priest) on Sep 17, 2013 at 13:54 UTC
    How many random numbers do you need for the unit tests? More specifically, is the number small enough that you could create a file of them as data for said tests?
      I need it in several places, 100 or 1000 probably. Yes, creating a file would work (for now, not sure about future).
Re: Predictable random sequence
by Corion (Patriarch) on Sep 17, 2013 at 13:39 UTC

    srand will give you a predictable sequence of error numbers for each version+compiler+platform of Perl. It does not guarantee cross-platform identity. For example on Windows, the (current) random number generator only has 15 bits of entropy, so numbers on Windows aren't that random.

      Thanks. That's sad. So I still searching for another approach then.

        Depending on your needs, you might get away by not using Perls random number generator and instead using Math::Random::MT - this would give you more randomness on Windows too, and maybe even predictability across more parameters.

        Personally, I would aim for centralizing the generation of such number sequences and passing either the generator or the number(s) around as parameters. That way, you can better control what number gets used where.

Re: Predictable random sequence
by kennethk (Abbot) on Sep 17, 2013 at 14:37 UTC
    Lots of ways to do it, depending on needed quality. The one I use for cross-platform stability in testing is the Middle-square method. It's actually a pretty bad RNG if you need any real rigor or a very large number of points, but it's fast, intuitive and insensitive to round-off.
    my $seed = 21324556; sub _prng { # von Neumann middle-square method return 1e-8*($seed = int(1e-4 * $seed**2) % 1e8); }
    This seed converges to zero after 16348 iterations, with an average in that population of 0.5002 and a standard deviation of 0.2878. If you go for more than 8 digits of precision, you will start seeing machine dependence.

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Predictable random sequence
by BrowserUk (Patriarch) on Sep 17, 2013 at 14:36 UTC

    Use Math::Random::MT. For any given seed and argument, it will produce the same sequence of random numbers.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
        but seems it's not cleared in documentation that sequence is always same for same seed.

        It does, else it wouldn't be the Mersenne Twister.

        Update: Results from a 32-bit and a 64-bit build of Perl (on windows):

        C:\test>\perl32\bin\perl -MMath::Random::MT=rand,srand -le"srand(1); p +rint rand(12345) for 1 .. 50" 0.417021998437122 0.99718480813317 0.720324489288032 0.932557361200452 + 0.000114381080493331 0.128124447772279 0.302332567749545 0.999040515394881 0.14675589255057 +3 0.236088976264 0.0923385955393314 0.39658072614111 0.186260211281478 0.38791074021719 +4 0.345560725079849 0.66974604036659 0.396767468657345 0.935539072612301 0.538816731888801 + 0.846310918219388 0.419194519054145 0.313273512991145 0.685219500679523 0.52454816270619 +6 0.204452248988673 0.44345289375633 0.878117437008768 0.22957722004503 0.027387595968321 +0.534413907211274 0.670467507559806 0.913962022401392 0.417304804315791 0.45720480917953 + 0.558689827332273 0.430698570096865 0.140386936953291 0.939127794932574 0.19810148328542 +7 0.778389235492796 0.800744566367939 0.715970510849729 0.968261571833864 0.80275750323198 +7 0.313424184685573 0.0928008102346212 0.692322616931051 0.518152548233047 0.8763891460839 +66 0.865020245313644 C:\test>\perl64\bin\perl -MMath::Random::MT=rand,srand -le"srand(1); p +rint rand(12345) for 1 .. 50" 0.417021998437122 0.99718480813317 0.720324489288032 0.932557361200452 + 0.000114381080493331 0.128124447772279 0.302332567749545 0.999040515394881 0.14675589255057 +3 0.236088976264 0.0923385955393314 0.39658072614111 0.186260211281478 0.38791074021719 +4 0.345560725079849 0.66974604036659 0.396767468657345 0.935539072612301 0.538816731888801 + 0.846310918219388 0.419194519054145 0.313273512991145 0.685219500679523 0.52454816270619 +6 0.204452248988673 0.44345289375633 0.878117437008768 0.22957722004503 0.027387595968321 +0.534413907211274 0.670467507559806 0.913962022401392 0.417304804315791 0.45720480917953 + 0.558689827332273 0.430698570096865 0.140386936953291 0.939127794932574 0.19810148328542 +7 0.778389235492796 0.800744566367939 0.715970510849729 0.968261571833864 0.80275750323198 +7 0.313424184685573 0.0928008102346212 0.692322616931051 0.518152548233047 0.8763891460839 +66 0.865020245313644

        You should get the same everywhere regardless of platform or build.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Predictable random sequence
by Athanasius (Archbishop) on Sep 17, 2013 at 13:55 UTC

    The Test::Random module may be what you’re looking for. But note that you have to use it instead of srand — because “If something in your code calls srand() all bets are off.”

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      No, it's based on srand().
      This module is useful if you use real random numbers in test, and wish to reproduce test failure with same seed (you still need same version+compiler+platform).
      In my case I need same numbers each run.
Re: Predictable random sequence
by Your Mother (Archbishop) on Sep 17, 2013 at 14:42 UTC

    While this is an interesting problem and I liked the answers, especially hdb's, it seems disconnected from your actual need. Predictable, randomized integers for use in unit tests, never changing. It would take one pass with int(rand(...)) for 1 .. $whatever to build a fixture and be done with it; and not risk introducing complications or dependencies on new code. The more code a test has the worse I think.

    Update: keszler basically said this already. DERP.

      I agree that the discussion is interesting and I also agree that generating once a file of pseudo-random numbers is probably the best solution for garanteeing reproductible results in tests.