Przykład wykorzystania generate i generate_n
/**
Bernard Fugas E1-ISIII
Przykład wykorzystania szablonów generate oraz generate_n
W skrócie:
Szablony generate i generate_n pozwalają przypisać wartości zwracane
przez bezargumentową funkcję lub obiekt funkcyjny do tablicy
lub kontenera udostępniającego iterator.
*/
#include <ostream>
#include <algorithm> // tu jest definicja generate i generate_n
#include <vector>
#include "boost/random.hpp" //generator liczb losowych - użyty w przykładzie
using namespace std;
/** GeneticAlgorithm to klasa realizująca przykładowy algorytm genetyczny.
Nie wnikamy w jego implementację.
Istotne jest to, że populacja początkowa składa się nie tylko z osobników
wygenerowanych losowo, ale również ze znanych "w miarę dobrych" rozwiązań
zwanych "ekspertami".
Szablony generate oraz generate_n pozwalają w wygodny sposób stworzyć
populację początkową o zadanej procentowej ilości ekspertów.
Przykłady użycia w funkcjach createGeneration0()
oraz operator<<(ostream& os, const GeneticAlgorithm& ga)
*/
class GeneticAlgorithm {
public:
/** Przykładowa klasa osobnika używanego w algorytmie genetycznym */
class Individual {
unsigned char chromosome_; //w rzeczywistym algorytmie byłaby to co najmniej tablica bajtów
public:
Individual() {}
Individual(unsigned char chromosome) : chromosome_(chromosome) {}
friend class GeneticAlgorithm;
friend ostream& operator<<(ostream& os, const Individual& i);
};
/** Obiekt funkcyjny generujący liczby pseudolosowe */
class RandomIndividualGenerator {
boost::mt19937 state_;
boost::uniform_int<unsigned> rng_;
public:
RandomIndividualGenerator(unsigned long state) : state_(state), rng_(0,255) {}
Individual operator() (void) { return Individual( static_cast<unsigned char>(rng_(state_)) ); }
};
////Definicje typów
typedef vector<Individual> Individuals;
typedef vector<Individual>::iterator IndividualsIt;
typedef vector<Individual>::const_iterator IndividualsCit;
////Metody publiczne
/**
@brief Konstruktor algorytmu.
@param[in] population_size Rozmiar populacji
@param[in] expert_percent Procent ekspertów w populacji początkowej
*/
GeneticAlgorithm(unsigned long seed, size_t population_size, size_t expert_percent) :
rig_(seed),
population_size_(population_size),
expert_percent_(expert_percent)
{}
/** @brief Uruchamia szkielet algorytmu. */
void run(void) {
bool finish_condition = false;
createGeneration0(); //stwórz populację początkową
while( !finish_condition ) {
//korzystaj z operatorów genetycznych
//selekcja, krzyżowanie, mutacja, ...
//w w którejś z iteracji...
finish_condition = true;
}
}
private:
/**
@brief Zwraca osobników "ekspertów" - znane, dość dobre rozwiązania
@return Nowy osobnik "ekspert"
@note Eksperci tworzeni są zgodnie z jakąś zależnością funkcyjną.
*/
static Individual getExpert(void) {
static unsigned char i = 0x77;
++i;
return Individual(i);
}
/**
@brief Tworzy populację początkową
@note Przykład użycia generate
*/
void createGeneration0(void) {
individuals_ = Individuals(population_size_);
size_t experts_num = population_size_*expert_percent_/100; //liczba ekspertów
IndividualsIt expertsEnd = individuals_.begin() + experts_num; //iterator "końca ekspertów"
/**************************************************************************************
void generate(_FwdIt _First, _FwdIt _Last, _Fn0 _Func)
Pozwala przypisać elementom od iteratora _First (włącznie z _First)
do _Last (bez _Last) kolejne wartości funkcji _Func
**************************************************************************************/
//generuj ekspertów
//zgodnie z zależnością funkcyjną; korzystając z funkcji
generate(individuals_.begin(), expertsEnd, getExpert);
//generuj losowe osobniki
//losowo; korzystając z funktora
generate(expertsEnd, individuals_.end(), rig_);
//można sobie wyobrazić jeszcze inne rodzaje tworzonych w ten sposób osobników...
}
Individuals individuals_; /**< wektor osobników w populacji */
size_t population_size_; /**< rozmiar populacji */
size_t expert_percent_; /**< procentowy udział ekspertów w populacji początkowej */
RandomIndividualGenerator rig_; /**< generator losowych osobników (obiekt funkcyjny) */
friend ostream& operator<<(ostream& os, const GeneticAlgorithm& ga);
friend class Individual;
}; //class GeneticAlgorithm
/**
@brief Zaimplementowany w celu łatwego wyświetlenia rezultatu
*/
ostream& operator<<(ostream& os, const GeneticAlgorithm::Individual& individual) {
os.setf(ios::hex, ios::basefield);
os.width(2);
os.fill('0');
return os << static_cast<unsigned>(individual.chromosome_);
}
/**
@brief Wyświetla bieżącą populację.
Dla celów dydaktycznych rozpoczyna od wyświetlenia sekwencji losowych osobników.
@note Przykład użycia generate_n
*/
ostream& operator<<(ostream& os, const GeneticAlgorithm& ga) {
/**************************************************************************************
generate_n(_OutIt _Dest, _Diff _Count, _Fn0 _Func)
Pozwala przypisać n elementom począwszy od _First
kolejne wartości zwracane przez _Func
Uwaga: tego nie da się wykonać za pomocą generate
**************************************************************************************/
os << "Losowe liczby:" << endl;
generate_n(ostream_iterator<GeneticAlgorithm::Individual>(os, " "), 10, ga.rig_);
os << endl << "Populacja:" << endl;
for(GeneticAlgorithm::IndividualsCit it=ga.individuals_.begin(); it != ga.individuals_.end(); ++it)
os << *it << endl;
return os;
}
// ================== main ================
int main(/*int argc, char** argv*/) {
size_t population_size = 20; /** < całkowity rozmiar populacji */
size_t expert_percent = 10; /** < procentowa ilość ekspertów w populacji początkowej */
unsigned long seed = 0;
GeneticAlgorithm ga(seed, population_size, expert_percent); //stwórz algorytm
ga.run(); //i go uruchom
//...
cout << ga; //wyświetl populację
}