Doc. no.: N4217
Date: 2014-10-08
Project: Programming Language C++, SG6: Numerics
Reply-to: Zhihao Yuan <zy at miator dot net>

std::rand replacement, revision 1

Changes since N3796

Motivation

We want to deprecate the std::rand friends, while “deprecation without a replacement” is a valid concern. This paper

  1. Proposes replacement to the std::rand friends. Despite of the security issues, std::rand is considered both handy and useful as a global uniform random number generator.

  2. Exposes the most widely-used combo in C++11 <random> without pushing the users to learn the whole design. Smoothing the learning curve can usually optimize the acceptance.

Design Decisions

std::rand is a self-contained interface, so its replacement should be independent as well. In addition, I expect the interface to correctly expose the functionalities of <random> and lead to more robust and secure programs. The proposed replacement is

Two variants for the shuffling and sampling algorithms without the explicit URNG parameter are also proposed.

Example

std::randint(0, 6);  // randomly seeded

// in another thread
std::seed_init();    // default_seed
std::shuffle(begin(v), end(v));

Wording

Change 26.5.2 rand.synopsis:

namespace std {

 // 26.5.7.2, function template generate_canonical
 template<class RealType, size_t bits, class URNG>
   RealType generate_canonical(URNG& g);
// 26.5.7.3, function template randint
template<class IntType>
  IntType randint(IntType a, IntType b);

// 26.5.7.4, seeding the per-thread engine
void seed_init();
void seed_init(default_random_engine::result_type value);
 // 26.5.8.2.1, class template uniform_int_distribution
 template<class IntType = int>
   class uniform_int_distribution;

} // namespace std

New section 26.5.7.3 rand.util.randint:

26.5.7.3 function template randint

All functions instantiated from the template described in this section share the same default_random_engine for a given execution of a thread; the random engine is non-deterministically seeded during the initialization if no call to any function described in section 26.5.7.4 in the same thread is sequenced before the random engine generating the first result. Such a random engine shall be maintained separately for each thread. [Note: The call expressions from different threads shall not be able to observe the same pseudo random number sequence in a deterministic way. –end note]

template<class IntType>
  IntType randint(IntType a, IntType b);

Requires: a b

Effects: Produce a random integer i, a ≤ i ≤ b, from a uniform_int_distribution<IntType> (26.5.8.2.1).

Returns: i.

New section 26.5.7.4 rand.util.seed_init:

26.5.7.4 seeding the per-thread engine

void seed_init();
void seed_init(default_random_engine::result_type value);

Requires: The random engine defined in section 26.5.7.3 in the same thread has not been seeded.

Effects: The first form seeds the engine with default_random_engine::default_seed. The second form seeds the engine with value.

Change 25.1 algorithms.general:

Header <algorithm> synopsis

 // 25.3.12, shuffle:
template<class RandomAccessIterator>
  void shuffle(RandomAccessIterator first, RandomAccessIterator last);
 template<class RandomAccessIterator, class UniformRandomNumberGenerator>
   void shuffle(RandomAccessIterator first,
                RandomAccessIterator last,
                UniformRandomNumberGenerator&& g);

Change 25.3.12 alg.random.shuffle:

template<class RandomAccessIterator>
  void shuffle(RandomAccessIterator first, RandomAccessIterator last);
 template<class RandomAccessIterator, class UniformRandomNumberGenerator>
   void shuffle(RandomAccessIterator first,
                RandomAccessIterator last,
                UniformRandomNumberGenerator&& g);

Remarks: To the extent that the implementation of this function makes use of random numbers, the object g shall serve as the implementation’s source of randomness in the second form, so does the random engine defined in section 26.5.7.3 in the same thread for the first form.

The following wording is relative to N4082 fund.ts.

Change 10.3 alg.random.sample:

template<class PopulationIterator, class SampleIterator, class Distance>
SampleIterator sample(PopulationIterator first, PopulationIterator last,
                      SampleIterator out, Distance n);
 template<class PopulationIterator, class SampleIterator,
          class Distance, class UniformRandomNumberGenerator>
 SampleIterator sample(PopulationIterator first, PopulationIterator last,
                       SampleIterator out, Distance n,
                       UniformRandomNumberGenerator&& g);

Remarks:

Sample Implementation

A sample implementation is available at https://github.com/lichray/randint.

Bikeshedding

First of all, overloading std::rand is not an option. User may deem std::rand() as a “safe” variant to the new interface.

Collected so far:

Acknowledgments

Hans Boehm, who emphasized the importance of enforcing the per-thread random engine more than once.

Stephan T. Lavavej, who carefully reviewed this paper and provided many corrections.

Walter E. Brown, who drafted the paper[1] which contains basically the same thought.

And many others who joined the std::rand related discussions on c++std-lib and c++std-lib-ext mailing lists.

References

[1] Brown, Walter E. N3742 Three <random>-related Proposals, v2. http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3742.pdf

[2] random – Generate pseudo-random numbers. “The Python Standard Library”. http://docs.python.org/2/library/random.html