Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Re: Old random number generator

by haukex (Archbishop)
on May 09, 2020 at 21:27 UTC ( [id://11116626]=note: print w/replies, xml ) Need Help??


in reply to Old random number generator

Is there a way to get the old behavior back?

From what little I can tell from this, it may still be possible to compile Perl on Windows to use the old way. But that seems overly complex.

I have a few programs that depend upon generating repeatable pseudorandom number sequences.

The new random number generator still does this, it's even more reliable because it'll be the same no matter what platform. So I suspect that's not really your point - I suspect you've got code that is depending on a certain sequence of random numbers? Note that, in general, this isn't reliable anyway, since AFAICT, Perl before 5.20 didn't guarantee a certain algorithm to be used. What you probably really want is a PRNG with a known algorithm.

Before 5.20, srand 0;print rand in Strawberry Perl always gave me 0.00115966796875.

Well, the following appears to work (after installing Microsoft Visual C++ 2010 Redistributable Package (x64); I'm not a Windows expert so maybe there's a better way*) - at least for the single value you provided. But again, you're probably better off choosing a known PRNG instead, possibly from CPAN.

use warnings; use strict; use Win32::API; my $srand = Win32::API::More->new( 'msvcr100', 'void srand(unsigned int seed)' ) or die "Error: ".Win32::FormatMessage(Win32::GetLastError()); my $rand = Win32::API::More->new( 'msvcr100', 'int rand()' ) or die "Error: ".Win32::FormatMessage(Win32::GetLastError()); $srand->Call(0); print $rand->Call()/(1<<15), "\n"; # 0.00115966796875

* Update: I can confirm that 'msvcrt' instead of 'msvcr100' works for me as well, thanks syphilis! /Update

And after a bit of digging, the Linear congruential generator that Windows uses can be reimplemented in Perl:

sub winrand { use feature 'state'; state $r = 0; # seed $r = ( 214013*$r + 2531011 ) & 0xFFFFFFFF; return ($r>>16)&0x7FFF; }

The output of this function can be used in the same way, i.e. the first call to winrand()/(1<<15) also produces 0.00115966796875, and further calls to winrand() produce the same as $rand->Call() in the above code. Note I've only tested this on a 64-bit Perl.

Replies are listed 'Best First'.
Re^2: Old random number generator
by syphilis (Archbishop) on May 10, 2020 at 00:47 UTC
    I think that will fit Pascal666's requirement splendidly.

    You probably don't need to install anything to get the script to work.
    IIRC, all windows systems come with msvcrt.dll - and modifying the script to use 'msvcrt.dll' instead of 'msvcr100.dll' worked fine for me.

    Cheers,
    Rob
Re^2: Old random number generator
by Pascal666 (Scribe) on May 10, 2020 at 02:45 UTC

    Wow. Thank you. Yes, I need a PRNG that outputs the same numbers as the old one. If I can implement it in Perl so that I don't have to worry about it ever changing again and it is the same on multiple platforms, awesome!

    winrand()/(1<<15) does appear to output the correct test numbers with my test seeds. Unfortunately, I can't figure out how to use winrand(EXPR) the same way I would use rand(EXPR) ("returns a random fractional number greater than or equal to 0 and less than the value of EXPR").

    For example, srand 0;print int(rand(99)) for 0..9 must always yield 023647263525973192.

      For example, srand 0;print int(rand(99)) for 0..9 must always yield 023647263525973192

      This outputs correctly for me:
      use warnings; use strict; my_srand(0); print int(my_rand(99)) for 0..9; sub my_srand { use Win32::API; my $srand = Win32::API::More->new( 'msvcrt', 'void srand(unsigned int seed)' ) or die "Error: ".Win32::FormatMessage(Win32::GetLastError()); $srand->Call($_[0]); } sub my_rand { my $rand = Win32::API::More->new( 'msvcrt', 'int rand()' ) or die "Error: ".Win32::FormatMessage(Win32::GetLastError()); return int(($rand->Call()) * $_[0] / 32767); }
      It outputs 023647263525973192
      The only problem is that it calls my_srand and my_rand, but I gather that the requirement is that it instead call srand and rand.
      I don't know how/if the builtin srand and rand can be overridden to point to my_srand and my_rand.

      Cheers,
      Rob
        Thank you. I don't mind calling my_srand and my_rand (no need to override srand and rand), but I would prefer a solution that works on Linux as well. The * $_[0] / 32767 part appears to be what I needed. print int(winrand()*99/32767) for 0..9 appears to work beautifully. Thank you everyone for your assistance!
      #!/usr/bin/perl use warnings; use strict; { my $seed = 0; sub win_srand { $seed = shift } sub win_rand { my ($max) = @_; $seed = ( 214013 * $seed + 2531011 ) & 0xFFFFFFFF; return $max * (($seed >> 16) & 0x7FFF) / (1 << 15) } } win_srand(0); print int(win_rand(99)) for 0..9; # 023647263525973192.
      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11116626]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2024-04-26 01:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found