通常将随机生成器和distrubitons封装到类模板中

时间:2018-03-22 08:49:20

标签: performance templates random variadic-templates c++17

我之前在我的一个旧库中有一组类用于处理Visual Studio 2008 - 2010在2012年发布时常见的随机数生成器和发行版。我正在开始一个新项目在Visual Studio 2017中,所以我决定将我原来的版本移植到。

这给了我太多的错误。所以我不得不修改原来的类,当我尝试使用上面的类的泛型函数时,由于你不能部分专门化函数模板,我最终面临一些挫折。经过几天的挫折之后;我重新开始并重写全班。使用C ++ 11和C的一些新功能。更高;我能够使用可变参数模板,一旦我使用了所需的语法,它就真正简化了事情。所以现在你有了一些历史,我可以继续我的工作实例。

我知道这个班Generator在我所知的情况下没有错误地工作;它编译,构建和运行没有错误,我已经使用模板类型T测试了一些不同的引擎或生成器组合,不同的播种技术和不同的发行版。

#ifndef GENERATOR_H
#define GENERATOR_H

#include <limits>
#include <chrono>
#include <random>
#include <type_traits>

enum SeedType { USE_CHRONO_CLOCK, USE_RANDOM_DEVICE, USE_SEED_VALUE, USE_SEED_SEQ };

template<class Engine, class Type, template<typename> class Distribution>
class Generator {
public:
    using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
        std::chrono::high_resolution_clock,
        std::chrono::steady_clock>;

private:
    Engine _engine;
    Distribution<Type> _distribution;
    Type _value;

public:    
    template<class... Params>
    explicit Generator( Engine engine, Params... params ) : _engine( engine ) {
        _distribution = Distribution<Type>( params... );
    }

    void seed( SeedType type = USE_RANDOM_DEVICE, std::size_t seedValue = 0, std::initializer_list<std::size_t> list = {} ) {
        switch( type ) {
            case USE_CHRONO_CLOCK:  { _engine.seed( getTimeNow() );  break; }
            case USE_RANDOM_DEVICE: { std::random_device device{};
                                      _engine.seed( device() );      break; }
            case USE_SEED_VALUE:    { _engine.seed( seedValue );     break; }
            case USE_SEED_SEQ:      { std::seed_seq seq( list );
                                      _engine.seed( seq );           break; }
        }
    }

    void generate() { _value = _distribution( _engine ); }

    Type getGeneratedValue() const { return _value; }

    Distribution<Type> getDistribution() const { return _distribution; }

    std::size_t getTimeNow() {
        std::size_t now = static_cast<std::size_t>(Clock::now().time_since_epoch().count());
        return now;
    }    
};

#endif // !GENERATOR_H

使用它就像这样说明一些例子:

#include <iostream>
#include <iomanip>
#include <vector>
#include "generator.h"

int main() {            
    // Engine, Seeding Type, & Distribution Combo 1
    std::mt19937 engine1;
    Generator<std::mt19937, short, std::uniform_int_distribution> g1( engine1, 1, 100 );
    g1.seed( USE_RANDOM_DEVICE );

    std::vector<short> vals1;
    for( unsigned int i = 0; i < 200; i++ ) {
        g1.generate();
        auto v = g1.getGeneratedValue();
        vals1.push_back( v );
    }

    int i = 0;
    for( auto& v : vals1 ) {

        if( (i % 10) != 0 ) {
            std::cout << std::setw( 3 ) << v << " ";
        } else {
            std::cout << '\n' << std::setw( 3 ) << v << " ";
        }       
        i++;
    }
    std::cout << "\n\n";

    // Engine, Seeding Type, & Distribution Combo 2
    std::ranlux48 engine2;
    std::initializer_list<std::size_t> list2{ 3, 7, 13, 17, 27, 31, 43 };   
    Generator<std::ranlux48, unsigned, std::binomial_distribution> g2( engine2, 50, 0.75 );
    g2.seed( USE_SEED_SEQ, std::size_t(7), list2 );

    std::vector<unsigned> vals2;
    for( int i = 0; i < 200; i++ ) {
        g2.generate();
        auto v = g2.getGeneratedValue();
        vals2.push_back( v );
    }

    i = 0;
    for( auto& v : vals2 ) {    
        if( (i % 10) != 0 ) {
            std::cout << std::setw( 3 ) << v << " ";
        } else {
            std::cout << '\n' << std::setw( 3 ) << v << " ";
        }
        i++;
    }
    std::cout << "\n\n";

    // Engine, Seeding Type, & Distribution Combo 3
    std::minstd_rand engine3;
    Generator<std::minstd_rand, float, std::gamma_distribution> g3( engine3, 0.22222f, 0.7959753f );
    g3.seed( USE_CHRONO_CLOCK );

    std::vector<float> vals3;    
    for( int i = 0; i < 200; i++ ) {
        g3.generate();
        auto v = g3.getGeneratedValue();
        vals3.push_back( v );
    }

    i = 0;
    for( auto& v : vals3 ) {

        if( (i % 5 ) != 0 ) {
            std::cout << std::setw( 12 ) << v << " ";
        } else {
            std::cout << '\n' << std::setw( 12 ) << v << " ";
        }
        i++;
    }
    std::cout << "\n\n";    

    std::cout << "\nPress any key and enter to quit.\n";
    std::cin.get();

    return 0;
}

这是一种通用封装随机发生器和放大器的适当方法吗?分布?我有什么“陷阱”吗?最后,为了提高效率,这可以改进吗?或者在Code Review上提出这个问题会更合适吗?

0 个答案:

没有答案