2 releases
0.1.1 | Feb 4, 2024 |
---|---|
0.1.0 | Feb 4, 2024 |
#1250 in Algorithms
31KB
562 lines
pyrand
Pure rust implementation of (parts of) python's random module with compatible PRNG behaviour: seeding with equivalent values will yield identical PRNG output. So running
import random
rng = random.Random("Pizza")
rng.choices(range(20), k=5)
will yield [3, 3, 15, 2, 16]
and running the equivalent
use pyrand::{PyMt19937, PySeedable, RandomChoiceIterator};
let rng = &mut PyMt19937::py_seed("Pizza");
assert_eq!((0..20).choose(rng).take(5).collect::<Vec<_>>(), vec![3, 3, 15, 2, 16]);
will also yield [3, 3, 15, 2, 16]
.
This seems pretty nieche. Why does this have to be a thing?
Assume you've written some python code using (pseudo-)random numbers like for example
import random
random.seed(input("Enter seed: "))
# run some computations using random numbers
...
result = random.random()
print(f"Your result is: {result}")
and your users somehow end up depending on that result in some way. Then your app has to consistently return the same exact values if the user provides the same input. This binds you very closely to python and its current random
implementation.
I actually ran into the unfortunate case of having some Python code where this was the case. I wanted to rewrite that code in Rust and kind off couldn't without also pulling in Python to handle the PRNG part (or statically linking parts of CPython or whatever) - which would've killed the whole rewrite. So long story short: I reimplemented the central functionality of python's random number generation code in rust, making sure to retain constistent output between the two implementations.
If you actually need this kind of compatibility for some of the functions I haven't implemented yet feel free to implement them and open a pull request - or file an issue on the GitHub repo.
rand
support
There are (optional) implementations for the rand
traits but they might reimplement some "natively" available generators on top of the basic internal generator - and do so differently to how they're implemented in python. This means that if you use the rand
interface to the generator you have to verify that you actually get matching output for the particular case you have.
Implementation details
The implementation is based off _randommodule.c
and random.py
and I tried to retain most function and variablenames etc. from there. The basic algorithm is a 32-bit Mersenne Twister (MT19937).
Dependencies
~1MB
~25K SLoC