boost random + polymorphie



  • hallo

    ich schreibe zur zeit ein programm zur simulation von verteilten systemen, brauche also pseudo-zufallszahlen - ist ja kein prob, gibts ja boost...

    allerdings brauche ich n verschiedene generatoren und der jeweilige algorithmus soll geändert werden können - hier mal mein Versuch (der mir am Anfang relativ übersichtlich und elegant erschien, jetzt aber eher total hässlich und vor allem kommt eben ne Fehlermeldung (siehe unten)):
    include-guards und namespaces hab ich mal weggemacht (die meisten includes hab ich aber gelassen)

    hier mal das grundgerüst meiner konstruktion - der generator soll eine Nachfrage simulieren:

    /*randomcustomer.h*/
    
    #include "customer.h"
    #include "random.h"
    
    template <typename Tcapacity, typename Trandom_generator /*TRandoms in "random.h"*/>
    class TRandomCustomer : public TCustomer_Base <Tcapacity>
    {
    private:
    	Trandom_generator number_generator; //Tcapacity /*random_nr [0,max)*/ operator () (Tcapacity max) /*(non-const)*/
    public:
    	TRandomCustomer (Tcapacity max_Review, const Trandom_generator &_number_generator)
    		: TCustomer_Base (max_Review), number_generator (_number_generator)
    	{}
    
    	const Trandom_generator& GetRandomNumberGenerator()
    		{ return number_generator; }
    
    	Tcapacity GetCurrentReview()
    	{
    		return number_generator(GetMaxReview() + 1); //da [0, max)
    	}
    };
    
    /*customer.h*/
    template <typename Tcapacity>
    class TCustomer_Base
    {
    private:
    	Tcapacity maxReview;
    public:
    	TCustomer_Base(Tcapacity _maxReview)
    		: maxReview (_maxReview)
    	{}
    
    	Tcapacity GetMaxReview() const
    		{ return maxReview; }
    	virtual Tcapacity GetCurrentReview() = 0;
    	virtual ~TCustomer_Base() {}
    };
    
    /*random.h*/
    
    #include <boost/random.hpp>		//our random algorithms
    #include <string.h>			//stricmp
    #include <string>				//std::string
    #include <vector>				//std::vector
    
    template <typename Tengine, typename Tgenerator_seed, typename Tresult>
    class TRandom //ist eigtl nur nen kleiner wrapper für das boost zeugs ^^
    {
    public:
    	typedef boost::uniform_real <> Tdistribution;
    	typedef boost::variate_generator <Tengine, Tdistribution> Tgenerator;
    private:
    	Tengine engine;
    	Tgenerator generator;
    public:
    	TRandom (Tgenerator_seed seed)
    		: engine ( boost::uint32_t (seed) ), generator ( engine, Tdistribution (0, 1) )
    	{}
    
    	//get a value from the interval [0, multiplicator)
    	template <typename Tmultiplicator>
    	Tresult operator () (Tmultiplicator multiplicator)
    	{
    		return static_cast <Tresult> ( generator() * multiplicator );
    	}
    };
    
    template <typename Trandom_seed, typename Tresult>
    struct TRandoms
    {
    	typedef TRandom <boost::mt19937, Trandom_seed, Tresult>							TRandom_MersenneTwister;
    	static const char *MersenneTwister; //in der *.impl definiert - wie auch der Rest...
    	typedef TRandom <boost::exponential_distribution <>, Trandom_seed, Tresult>		TRandom_ExpotentialDistribution;
    	static const char *ExpotentialDist;
    	typedef TRandom <boost::bernoulli_distribution <>, Trandom_seed, Tresult>		TRandom_BernoulliDistribution;
    	static const char *BernoulliDist;
    	typedef TRandom <boost::binomial_distribution <>, Trandom_seed, Tresult>		TRandom_BinomialDistribution;
    	static const char *BinomialDist;
    	typedef TRandom <boost::cauchy_distribution <>, Trandom_seed, Tresult>			TRandom_CauchyDistribution;
    	static const char *CauchyDist;
    	typedef TRandom <boost::geometric_distribution <>, Trandom_seed, Tresult>		TRandom_GeometricDistribution;
    	static const char *GeometricDist;
    	typedef TRandom <boost::gamma_distribution <>, Trandom_seed, Tresult>			TRandom_GammaDistribution;
    	static const char *GammaDist;
    	typedef TRandom <boost::lognormal_distribution <>, Trandom_seed, Tresult>		TRandom_LogNormalDistribution;
    	static const char *LogNormalDist;
    	typedef TRandom <boost::normal_distribution <>, Trandom_seed, Tresult>			TRandom_NormalDistribution;
    	static const char *NormalDist;
    	typedef TRandom <boost::poisson_distribution <>, Trandom_seed, Tresult>			TRandom_PoissonDistribution;
    	static const char *PoissonDist;
    	typedef TRandom <boost::triangle_distribution <>, Trandom_seed, Tresult>		TRandom_TriangleDistribution;
    	static const char *TriangleDist;
    
    	template <typename T>
    	static bool IsT(const char* name)
    	{
    		int res;
    		if (typeid(T) == typeid(TRandom_ExpotentialDistribution))
    			res = _stricmp (name, ExpotentialDist);
    		else if (typeid(T) == typeid(TRandom_BernoulliDistribution))
    			res = _stricmp (name, BernoulliDist);
    		else if (typeid(T) == typeid(TRandom_BinomialDistribution))
    			res = _stricmp (name, BinomialDist);
    		else if (typeid(T) == typeid(TRandom_CauchyDistribution))
    			res = _stricmp (name, CauchyDist);
    		else if (typeid(T) == typeid(TRandom_GammaDistribution))
    			res = _stricmp (name, GammaDist);
    		else if (typeid(T) == typeid(TRandom_GeometricDistribution))
    			res = _stricmp (name, GeometricDist);
    		else if (typeid(T) == typeid(TRandom_LogNormalDistribution))
    			res = _stricmp (name, LogNormalDist);
    		else if (typeid(T) == typeid(TRandom_MersenneTwister))
    			res = _stricmp (name, MersenneTwister);
    		else if (typeid(T) == typeid(TRandom_NormalDistribution))
    			res = _stricmp (name, NormalDist);
    		else if (typeid(T) == typeid(TRandom_PoissonDistribution))
    			res = _stricmp (name, PoissonDist);
    		else if (typeid(T) == typeid(TRandom_TriangleDistribution))
    			res = _stricmp (name, TriangleDist);
    		else
    			throw std::invalid_argument ("not a supported algorithm");
    
    		return !res;
    	}
    
    	static std::vector <std::string> GetAlgorithms ()
    	{
    		std::vector <std::string> R;
    
    		R.push_back (ExpotentialDist);
    		R.push_back (BernoulliDist);
    		R.push_back (BinomialDist);
    		R.push_back (CauchyDist);
    		R.push_back (GammaDist);
    		R.push_back (GeometricDist);
    		R.push_back (LogNormalDist);
    		R.push_back (MersenneTwister);
    		R.push_back (NormalDist);
    		R.push_back (PoissonDist);
    		R.push_back (TriangleDist);
    
    		return R;
    	}
    };
    

    dann noch der zweite teil in randomcustomer.h:

    namespace Customer
    {
    	template <typename Tcapacity, typename Trandom_generator>
    	void SetRandomAlgorithmExplicit (TCustomer_Base <Tcapacity> *&val, const Trandom_generator &gen)
    	{
    		TCustomer_Base <Tcapacity> *toswap = new TRandomCustomer <Tcapacity, Trandom_generator> (val->GetMaxReview(), gen);
    		std::swap (val, toswap);
    		delete toswap;
    	}
    
    	template <typename Tcapacity, typename Trandoms, typename Trandom_seed>
    	void SetRandomAlgorithm (TCustomer_Base <Tcapacity> *&val, const std::string &random_algo_name, Trandom_seed seed = Trandom_seed())
    	{
    		if (TRandoms<Trandom_seed, Tcapacity>::IsT <Trandoms::TRandom_BernoulliDistribution> (random_algo_name.c_str()))
    		{
    			SetRandomAlgorithmExplicit <Tcapacity, TRandoms<Trandom_seed, Tcapacity>::TRandom_BernoulliDistribution >
    				(val, TRandoms<Trandom_seed, Tcapacity>::TRandom_BernoulliDistribution (seed));
    		}
    	//usw...
    	}
    

    falls ihr euch den aufruf nicht vorstellen könnt:

    /*main.cpp*/
    
    int main()
    {
    	const std::vector <std::string> algos = TRandoms::GetAlgorithms();
    	for (std::vector <std::string>::const_iterator i(algos.begin()), e(algos.end()); i != e; ++i)
    		std::cout << *i << ENDL;
    	std::cout << std::endl;
    
    	TRandomSeed i (123456); //boost::uint32_t
    	TCapacity max (100); //signed int
    
    	std::vector <TCustomer_Base*> customers; //TCustomer_Base <my::TCapacity>
    	customers.push_back (
    		new TRandomCustomer <TRandoms::TRandom_MersenneTwister>::T ( max, TRandoms::TRandom_MersenneTwister (i) )
    	); //TRandoms <my::TRandomSeed, my::TCapacity>
    
    	std::string new_algo;
    	std::getline (std::cin, new_algo);
    	SetRandomAlgorithm <my::TCapacity, TRandoms, my::TRandomSeed> (customers.back(), new_algo);
    
    //aufräumen
    
    }
    

    Die aktuelle Fehlermeldung:

    1>f:\boost_1_37_0\boost_1_37_0\boost\random\detail\pass_through_engine.hpp(40) : error C2039: 'min' : is not a member of 'boost::bernoulli_distribution<>'
    1>        f:\boost_1_37_0\boost_1_37_0\boost\random\detail\pass_through_engine.hpp(40) : while compiling class template member function 'bool boost::random::detail::pass_through_engine<UniformRandomNumberGenerator>::min(void) const'
    1>        with
    1>        [
    1>            UniformRandomNumberGenerator=boost::bernoulli_distribution<>
    1>        ]
    1>        f:\boost_1_37_0\boost_1_37_0\boost\random\variate_generator.hpp(92) : see reference to class template instantiation 'boost::random::detail::pass_through_engine<UniformRandomNumberGenerator>' being compiled
    1>        with
    1>        [
    1>            UniformRandomNumberGenerator=boost::bernoulli_distribution<>
    1>        ]
    1>        ...\random.h(25) : see reference to class template instantiation 'boost::variate_generator<Engine,Distribution>' being compiled
    1>        with
    1>        [
    1>            Engine=boost::bernoulli_distribution<>,
    1>            Distribution=my::TRandom<boost::bernoulli_distribution<>,my::TRandomSeed,my::TCapacity>::Tdistribution
    1>        ]
    1>        ...\randomcustomer.h(50) : see reference to class template instantiation 'my::TRandom<Tengine,Tgenerator_seed,Tresult>' being compiled
    1>        with
    1>        [
    1>            Tengine=boost::bernoulli_distribution<>,
    1>            Tgenerator_seed=my::TRandomSeed,
    1>            Tresult=my::TCapacity
    1>        ]
    1>        ...\main.cpp(62) : see reference to function template instantiation 'void my::Customer::SetRandomAlgorithm<my::TCapacity,TRandoms,my::TRandomSeed>(my::TCustomer_Base<Tcapacity> *&,const std::string &,Trandom_seed)' being compiled
    1>        with
    1>        [
    1>            Tcapacity=my::TCapacity,
    1>            Trandom_seed=my::TRandomSeed
    1>        ]
    

    Danke schon mal für die Hilfe und vll ja doch auch nen schöneres Design ^^
    Ich hatte auch scho mal ne Frage dazu und da wurde unter anderem gefragt, warum ich so viele templates nutze (die den Code so hässlich machen):
    Weil ich die Größenordnungen nicht kenne und bevor ich es fest mache und dann heißt es wieder, dass die auch von Instanz zu Instanz unterschiedlich sein sollten, mach ich es lieber so und habe dann nicht ganz so viel zu ändern - so hab ich eben in der main.cpp erstmal 20Zeilen typedefs auf irgendwelche Templateklassen - und so sieht man wenigsten in der main-fkt gut durch ^^

    Einen nachteil, den ich auch gern nicht mehr hätte wäre, dass ich so ewig viel ändern muss, nur um später einen Algorithmus hinzuzufügen...

    bb


  • Administrator

    Ehm, lies die Dokumentation:
    http://www.boost.org/doc/libs/1_38_0/libs/random/random-distributions.html#bernoulli_distribution

    Die "Bernouilli Distribution" hat kein min oder max, da sie nur ein true oder false zurückgibt.
    Hätte man aber ohne Probleme aus der Fehlermeldung herausfinden können.

    Allerdings versteh ich den Code auch nicht so recht. Wieso verwendest du eine Distribution als Engine? Und deshalb ist mir auch nicht so klar, was dein genaues Ziel nun ist? Willst du unterschiedliche Engines haben oder willst du unterschiedliche Verteilungen verwenden?

    Und ja, ich stimme dir zu, der Code ist hässlich 🙂

    Grüssli



  • Dravere schrieb:

    Die "Bernouilli Distribution" hat kein min oder max, da sie nur ein true oder false zurückgibt.
    Hätte man aber ohne Probleme aus der Fehlermeldung herausfinden können.

    Japp - habe ich - dann hab ich Bernoulli rausgenommen, und dann hats die selbe Fehlermeldung direkt noch mal gegeben (eben mit der nächsten Engine)...

    Dravere schrieb:

    Wieso verwendest du eine Distribution als Engine?

    Wo denn? Oo

    Dravere schrieb:

    Und deshalb ist mir auch nicht so klar, was dein genaues Ziel nun ist? Willst du unterschiedliche Engines haben oder willst du unterschiedliche Verteilungen verwenden?

    Ich möchte unterschiedliche Verteilungen haben (in dem ich den Algorithmus dahinter ändere und zusätzlich noch die Wahl habe, den Seed neu zu setzen)

    bb


  • Administrator

    unskilled schrieb:

    Wo denn? Oo

    Zum Beispiel bei der bernouilli_distribution<> :
    typedef TRandom <boost::exponential_distribution <>, Trandom_seed, Tresult> TRandom_ExpotentialDistribution;
    TRandom erwartet als erster Template-Parameter die Engine und nicht eine Verteilung. Die Verteilung in TRandom ist immer uniform_real<> .

    unskilled schrieb:

    Ich möchte unterschiedliche Verteilungen haben (in dem ich den Algorithmus dahinter ändere und zusätzlich noch die Wahl habe, den Seed neu zu setzen)

    Das macht dein Code aber definitiv nicht. TRandom verwendet, wie gesagt, immer die uniform_real<> Verteilung.

    Grüssli



  • Dravere schrieb:

    unskilled schrieb:

    Wo denn? Oo

    Zum Beispiel bei der bernouilli_distribution<> :
    typedef TRandom <boost::exponential_distribution <>, Trandom_seed, Tresult> TRandom_ExpotentialDistribution;
    TRandom erwartet als erster Template-Parameter die Engine und nicht eine Verteilung. Die Verteilung in TRandom ist immer uniform_real<> .

    unskilled schrieb:

    Ich möchte unterschiedliche Verteilungen haben (in dem ich den Algorithmus dahinter ändere und zusätzlich noch die Wahl habe, den Seed neu zu setzen)

    Das macht dein Code aber definitiv nicht. TRandom verwendet, wie gesagt, immer die uniform_real<> Verteilung.

    Grüssli

    Verdammt - dann hab ichs wirklich total falsch verstanden gehabt (weil ich ma wieder zu faul war, alles durchzulesen ^^) - naja, dann kann ich wenigsten den Teil noch ma neu anfangen und hab dann vll auch endlich ma wieder schönerer Code ^^

    ty 🙂


Log in to reply