The basics of <random>

Erich Keane

Software Engineer, iCDG, Intel

Erich.Keane@verizon.net

rand() is wrong AND You're using it wrong!

  • Seeding
    • void srand(unsigned seed);
    • 32-bit seed value, very limited seed values
      • Only ~4.3 Billion states!-- Trivially calculable
      • Strangles better rand() implementations
    • srand(time(NULL));
      • 'time' only has a 1 Hz frequency!
      • VERY predictable
        • ENTIRE 32 bit space is only ~136 years
        • I KNOW your app seeded in the last:
          • Decade : ~315,360,000:  29 bits/entropy
          • Year:  ~31,536,000: 25 bits/entropy
          • Month: ~2,592,000: ~22 bits/entropy

rand() is wrong AND You're using it wrong!

  • Entropy
    • rand() returns int from 0->RAND_MAX
      • Many platforms: RAND_MAX == 32767
      • Many others scale to get that far!
    • History of short-comings, still many biased to even/odds
    • Most are Linear congruential engine
      • VERY Low quality random
    • COMPLETELY and predictably Deterministic!

rand() is wrong AND You're using it wrong!

  • Distribution
    • rand() % 1000
      • Biased for # < RAND_MAX % 1000
      • [0, 999] -> [0, 999]
      • [1000, 1999] -> [0, 999]
      • [32000, 32767] -> [0, 767] <-- MORE likely!
    • Floating-point has similar issues
      • Some values still more likely (see above!)
      • Some values not possible based on FP precision/mappings

Enter C++11: <random>

  • Reasonably easy to use
  • Provides a 'best try' at Non-deterministic behavior
    • std::random_device
  • Provides random-number engines
    • std::linear_congruential_engine
    • std::mersenne_twister_engine
    • std::subtract_with_carry_engine
  • Provides MANY Distributions!
    • Uniform
    • Bernoulli
    • Poisson
    • Normal
    • Sampling Distributions
  • TOGETHER, these combine for REALLY GOOD random

std::random_device

  • Non-deterministic (if possible)
    • Otherwise degrades to best-possible
    • g++: rd_rand instruction
    • clang: /dev/urandom
  • EASY to use!
  • Note: do NOT use %!
std::random_device rd; // Instantiates a RNG!
unsigned int rand_num = rd(); // generates a random number!

Engines

  • Completely customizable
  • Provides typedefs for commonly used configurations
    • std::linear_congruential_engine
      • minstd_rand0, minstd_rand
    • std::mersenne_twister_engine
      • mt19937, mt19937_64
  • Can be seeded with FULL entropy (std::seed_seq)
  • Gotcha: "Correctly" seeding difficult, see PCG or boost::random!
std::random_device rd; // Instantiates a RNG!
std::array <unsigned int, 624> entropy; // 624 is magic number for MT :(
std::generate(std::begin(entropy), std::end(entropy), std::ref(rd));
std::seed_seq seq{entropy}; // Seed Sequence Ready
std::mt19937 engine{seq}; // Create engine for MT random numbers

unsigned int rnd_val = engine(); // rnd_val is a random number!

Distributions

  • Works correctly!  Guarantees correct distribution
  • Uniform, Bernoulli, Poisson, Normal, Sample distributions available
std::random_device rd; // Instantiates a RNG!
std::array <unsigned int, 624> entropy; // 624 is magic number for MT :(
std::generate(std::begin(entropy), std::end(entropy), std::ref(rd));
std::seed_seq seq{entropy}; // Seed Sequence Ready
std::mt19937 engine{seq}; // Create engine for MT random numbers

std::uniform_int_distribution<int> int_dist{1,5};
int rand_int = int_dist(engine); // rand_int is [1,5]

std::uniform_real_distribution<double> double_dist{1, 1.5};
double rand_double = double_dist(engine); // [1, 1.5)

Still Some Problems:

  • Not Crypto-secure, except for deterministic random_device
  • Correctly seeding is REALLY hard (see examples in this presentation)
    • http://www.pcg-random.org/posts/cpp-seeding-surprises.html
    • Can use PCG, or Boost, both which make it easier
    • Alternatively, creating a seed_seq with a random device is pretty easy (See next slide!)

Still Some Problems:

Fixing the Seed Problem:

// Note: Not actually SeedSequence, 
// lacks default/init-list ctors and params method
template <class RandType>
class GenSeq
{
     public:
     using result_type = typename RandType::result_type;
     GenSeq(RandType& rt):_rt(rt){}
     size_t size() const{ return std::numeric_limits<size_t>::max();}
 
     template< class RandomIt>
     void generate( RandomIt begin, RandomIt end)
     {
         std::generate(begin, end, std::ref(_rt));
     }
     private:
     RandType& _rt;
};  
 
int main()
{
    std::random_device rd;
    GenSeq<std::random_device> seq { rd };    
    std::mt19937 engine { seq };    
    std::cout << engine() << '\n';
}

Q&A

Erich.Keane@verizon.net

Template-Templates

Erich Keane

Software Engineer, iCDG, Intel

Erich.Keane@verizon.net

What ARE Template-Templates?

  • Component of Template-Meta-Programming (TMP)
  • Allow matching of template-parameters of template-parameters!
  • Particularly useful with Variadic-Templates, less interesting otherwise thanks to type-traits

Simple Examples

  • Matching/using the internal types of a collection
  • A bit contrived, since ::value_type exists
template<template<typename, typename> class T, typename IT, typename Alloc>
void DoThingToVector(T<IT, Alloc> vect)
{
    std::cout << typeid(IT).name() <<std::endl;
}

int main()
{
    std::vector<int> iv; 
    std::vector<double> dv; 
    std::vector<std::string> sv; 
    DoThingToVector(iv);
    DoThingToVector(dv);
    DoThingToVector(sv);
}

// Results:
i
d
NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE

Simple Examples

  • Specialization based on sub-type!
template<template<typename, typename> class T, typename IT, typename Alloc>
void DoThingToVector(T<IT, Alloc> vect)
{
    std::cout << typeid(IT).name() <<std::endl;
}

template<template<typename, typename> class T, typename Alloc>
void DoThingToVector(T<std::string, Alloc> vect)
{
    std::cout << "std::string!" <<std::endl;
}

int main()
{
    std::vector<int> iv; 
    std::vector<double> dv; 
    std::vector<std::string> sv; 
    DoThingToVector(iv);
    DoThingToVector(dv);
    DoThingToVector(sv);
}

// Results:
i
d
std::string!

Variadic Examples

  • Match internal types of a Variadic-container
template<template<typename...> class T, typename FST, typename SCD, typename...Rest>
void PrintSecondType(T<FST,SCD,Rest...>)
{
    std::cout << typeid(SCD).name() << std::endl;
}

int main()
{
    std::tuple<int, double, std::string> tp1;
    std::tuple<int, float, std::string> tp2;
    std::tuple<int, char, std::string> tp3;

    PrintSecondType(tp1);
    PrintSecondType(tp2);
    PrintSecondType(tp3);
}


// Results:
d
f
c

Variadic Examples

  • Match Params to Function-Pointer-Wrapper
template<typename Func> 
struct func_get; 
 
template<template <typename...> class Func, typename... Args, typename Ret> 
struct func_get<Func<Ret(Args...)>> 
{ 
    template<size_t Index> 
    using argument_type = typename std::tuple_element<Index, std::tuple<Args...> >::type; 
}; 
 
int main() 
{ 
    std::function<int(double, float, char, int)> f; 
 
    static_assert(is_same<double,func_get<decltype(f)>::argument_type<0>>::value, "Failed!"); 
    static_assert(is_same<float,func_get<decltype(f)>::argument_type<1>>::value, "Failed!"); 
    static_assert(is_same<char,func_get<decltype(f)>::argument_type<2>>::value, "Failed!"); 
    static_assert(is_same<int,func_get<decltype(f)>::argument_type<3>>::value, "Failed!"); 
} 

Q&A

Erich.Keane@verizon.net

Random And Template-Templates

By Erich Keane

Random And Template-Templates

PDXCPP Slides covering the C++11 Random functionality as well as Template-Templates.

  • 852