[HN Gopher] C++ Seeding Surprises (2015)
___________________________________________________________________
C++ Seeding Surprises (2015)
Author : vsbuffalo
Score : 29 points
Date : 2025-06-25 16:11 UTC (3 days ago)
(HTM) web link (www.pcg-random.org)
(TXT) w3m dump (www.pcg-random.org)
| rramadass wrote:
| Relevant: A old Reddit discussion by the same author -
| https://old.reddit.com/r/cpp/comments/32u4m7/the_behavior_of...
| nynx wrote:
| Is it possible to initialize a prng in C++'s std correctly?
| on_the_train wrote:
| No one uses the <random> header as it's cursed and the usual
| cult of backwards compatibility ensures it'll stay that way.
|
| There are several high quality alternatives that people use.
| thechao wrote:
| I've found it easier to write my own PRNG than to use the
| std. Using the std PRNG is about as buggy as my
| implementation, so the trade-off is reasonable. I usually
| need non-cryptographically strong PRNGs, so xorshift128+ is
| sufficient.
| nynx wrote:
| How did it get into the standard then?
| loeg wrote:
| It was better than the bad, C interface LCG rand(), I
| guess. (There are LCG parameters that make for ~objectively
| better PRNGs than MT, but rand()'s parameters aren't great
| and its state is too small.)
| rramadass wrote:
| You need to understand PRNGs to answer that question. It is
| complicated and nothing to do with C++ language itself.
|
| Here is cppreference on PRNGs (note the various engines
| available) -
| https://www.cppreference.com/w/cpp/numeric/random.html You
| have to "know" how to combine the various options available
| to get an optimal sequence.
|
| The Mersenne Twister (MT) was one of the best engines and
| was the default in many other languages/packages too. See
| "Applications" section in wikipedia -
| https://en.wikipedia.org/wiki/Mersenne_Twister
|
| The author identified distribution problems with the 32-bit
| versions of MT (i am not sure whether similar problems
| exist with its 64-bit versions) and proposed a different
| one named "Permuted Congruential Generator (PCG)" which has
| now been adopted as the default by many of the
| languages/packages - https://en.wikipedia.org/wiki/Permuted
| _congruential_generato...
|
| As you can now appreciate, the subject is mathematically
| complicated and the defaults chosen by the language/package
| implementer becomes the "most commonly used" and hence
| reference case. While this is good enough for most "normal"
| applications if you are doing any special simulations
| (Monte Carlo or otherwise) and/or specific Numerical
| Computations it is your responsibility to understand what
| it is that you need and program accordingly using the
| various options (if available) or roll your own.
| o11c wrote:
| > (i am not sure whether similar problems exist with its
| 64-bit versions) and proposed a different one named
| "Permuted Congruential Generator (PCG)"
|
| The 64-bit version might be a bit faster (for certain
| workloads, on 64-bit hardware) than the 32-bit version,
| but still wastes the same space and has the same
| mathematical flaws.
|
| PCG is still not perfect (128-bit math hurts, though the
| new DXSM variant at least reduces that to 128x64), but
| its mathematical properties are nicer than the xor*
| family (its main competitor), and both families are miles
| ahead of any other RNG out there.
| rramadass wrote:
| It is understanding the non-trivial statistical
| properties (even at a simple conceptual level) that is of
| paramount importance. PRNG is one of the most difficult
| subjects in Numerical Computation and has nothing
| whatever to do with any language/package/library etc.
|
| From https://en.wikipedia.org/wiki/Pseudorandom_number_ge
| nerator
|
| _Even today, caution is sometimes required, as
| illustrated by the following warning in the International
| Encyclopedia of Statistical Science (2010).
|
| The list of widely used generators that should be
| discarded is much longer [than the list of good
| generators]. Do not trust blindly the software vendors.
| Check the default RNG of your favorite software and be
| ready to replace it if needed. This last recommendation
| has been made over and over again over the past 40 years.
| Perhaps amazingly, it remains as relevant today as it was
| 40 years ago._
| loeg wrote:
| Sadly, people that don't know better use std::mt19937 all the
| time :-(.
| on_the_train wrote:
| And the only reason is its cool name. Humans are weird
| loeg wrote:
| Well, it's in std. So there's an appeal to authority (the
| C++ language authors should be smart, right?) and
| convenience.
| on_the_train wrote:
| There are still others in <random>. Yet they always use
| the cool twister one with the funny numbers
| slavik81 wrote:
| What alternative within <random> would you recommend and
| why?
| slavik81 wrote:
| I used it because it was recommended by Stephan T.
| Lavavej, maintainer of Visual Studio's C++ Standard
| Library, in his "rand() Considered Harmful" talk, back
| when <random> was introduced. See 11m30s.
| https://youtu.be/LDPMpc-ENqY?t=10m50s
| loeg wrote:
| If you're aware/concerned about seeding, you probably aren't
| using the C++ std prng (mt19937) anyway -- other prngs have
| desirable properties like vastly smaller state, better
| performance, or cryptographic security.
| sltkr wrote:
| My standard code for doing this looks like this:
| #include <algorithm> #include <array> #include
| <random> std::mt19937 InitializeRng() {
| std::array<unsigned int, 624> seed_data;
| std::random_device dev;
| std::generate_n(seed_data.data(), seed_data.size(),
| std::ref(dev)); std::seed_seq
| seq(std::begin(seed_data), std::end(seed_data));
| return std::mt19937(seq); }
|
| This generates seed_seq with 19968 bits of random data, which
| is enough for the 19937 bits of Mersenne Twister internal
| state.
|
| Note that 19668 bits of random data is overkill; something like
| 256 or 128 bits would probably enough for practical purposes.
| But I believe there is no real need to limit the amount of data
| extracted from a random source. Modern operating systems are
| pretty good at generating large amounts of random data quickly.
| But if this is a concern, just change 624 to 4/8/16/32 for
| 128/256/512/1024 bits of entropy. In practice, I don't think
| you'll notice a difference either in randomness or
| initialization speed.
|
| edit: also, if performance is a concern, consider changing
| mt19937 to mt19937_64, which is the 64-bit variant of mt19937
| that is incompatible (generates different numbers) but is
| almost twice as fast on 64-bit platforms (i.e. most platforms
| today).
| cjfd wrote:
| This talks about 'bad' seeding quite a lot. But it really depends
| on what you need what is bad and what is good. Sometimes you need
| to have a reproducible program so you need to write the random
| number generator yourself and/or otherwise fix the algorithm.
| Then you can use '5' as the seed. This is quite often good enough
| for simulations. Sometimes you want to create cryptographic
| randomness. Then you need to somewhere find some seed of true
| randomness that is not guessable. In a computer game where the
| level needs some random elements just seeding the random number
| generator with the current time might be fine. And so on.
| quietbritishjim wrote:
| I don't think it disputes that sometimes using a fixed small
| number is a good seed for reproducibility. The point is just,
| if you do happen to be in the other camp, where you actually
| want to populate the full state of the RNG using hardware
| randomness, C++ does not give you an obvious way to do that.
| b0a04gl wrote:
| found one more flakiness over cross platform, when seed mt19937
| same way on linux and windows, same compiler, same code... but
| problem is std::random_device or libc internals differ under the
| hood. some platforms do random_device as true hardware entropy,
| others fake it or seed from diff system sources. so seed
| retrieved isn't stable cross platform. that means mt19937 starts
| from diff states, causing different random sequences
|
| it's not a bug in mt19937 itself, it's how random_device (or libc
| randomness) works differently across environments. makes cross
| platform tests flaky even when logic is rock solid
|
| >>
|
| std::random_device rd; // might differ per platform
|
| std::mt19937 gen(rd()); // seed depends on rd output
|
| std::uniform_int_distribution<> dist(1, 100);
|
| int random_number = dist(gen); // different on linux vs windows
| tho same code
| senkora wrote:
| Why not just run your tests with a fixed seed? e.g.
| std::mt19937 gen(42); std::uniform_int_distribution<>
| dist(1, 100); int random_number = dist(gen);
___________________________________________________________________
(page generated 2025-06-28 23:01 UTC)