TL;DR

Sometimes we just need some mixing up of stuff.

For a little diversion that Iâ€™m taking, I wanted a random number generator that could be easily ported to other languages too, so that I could get a consistent behaviour.

So it was natural for me to remember that the glorious Numerical Recipes in C had something about it (in Chapter 7), something actually suggested by Knuth himself for a 32-bit Pseudo-Random Number Generator (PNRG) using a recurrent function, with some help from H.W. Lewis (section Quick and Dirty Generators):

$R_{i+1} = R_i \cdot 1664525 + 1013904223 \pmod{2^{32}}$

This is nothing that should be used for anything related to security or betting, but itâ€™s good and simple enough for situation with much lower expectations.

Implementing it in Perl is pretty straightforward, as an iterator:

sub randomish_uint32_it ($seed = undef) {$seed = seed_to_num($seed) & 0xFFFFFFFF; return sub {$seed = ($seed * 1664525 + 1013904223) & 0xFFFFFFFF }; }  The function is a factory for generating an iterator, i.e. another function that will give us the next pseudo-random integer at every call. This factory expects to receive something as seed, so that using the same seed allows re-generating the same sequence (either in some later time, or in some other language). This is what I came up with for turning a string into a seed value useable in the function above: sub seed_to_num ($seed = undef) {
return time() unless defined $seed; return$seed if $seed =~ m{\A (?: 0 | [1-9]\d*) \z}mxs; my$val = 0;
$val = ($val << 8) | ord(substr($seed,$_)) for 0 .. length($seed) - 1; return$val;
}


Actually, it accepts something resembling a non-negative integer and returns it unchanged; turns strings into integers; returns the current epoch if the input is undefined.

For reasons I also needed to get a random bit; the suggestion in the text is to use a different integer for each bit, and trust the higher bits more than the lower ones, so hereâ€™s my approach with a wrapper iterator:

sub get_bit ($it,$max = 0xFFFFFFFF) { sub { 2 * $it->() >$max ? 1 : 0 } }


Now, this is technically less portable than the other (which is also more or less portable by itself). In particular, the multiplication by 2 might overflow on architecture that do not support 64-bit integers. Hence, in the specific case of the underlying generation, this is probably better:

sub get_bit ($it) { sub {$it->() & 0x80000000 ? 1 : 0 } }


i.e. it checks the higest bit and returns accordingly.

The corresponding implementation in Raku would be:

class Randomish {
has $!s; submethod TWEAK (:$seed = Nil) {
if (! defined($seed)) {$!s = DateTime.now.posix }
elsif ($seed ~~ m{^^ [ 0 | <[ 1..9 ]>\d* ]$$}) {$!s = $seed } else {$!s = $seed.combÂ».ord.reduce({($^a +< 8) +| $^b}) }$!s +&= 0xFFFFFFFF;
}

method uint32() { $!s = ($!s * 1664525 + 1013904223 ) +& 0xFFFFFFFF }
method bit() { self.uint32() +& 0x80000000 ?? 1 !! 0 }
}


Stay safe and stay random!

Comments? Octodon, , GitHub, Reddit, or drop me a line!