diff --git a/eo/src/eoProportionalSelect.h b/eo/src/eoProportionalSelect.h index f85321f44..8c01b7503 100755 --- a/eo/src/eoProportionalSelect.h +++ b/eo/src/eoProportionalSelect.h @@ -32,11 +32,13 @@ #include #include #include - +#include //----------------------------------------------------------------------------- /** eoProportionalSelect: select an individual proportional to her stored fitness value - + + Changed the algorithm to make use of a cumulative array of fitness scores, + This changes the algorithm from O(n) per call to O(log n) per call. (MK) */ //----------------------------------------------------------------------------- @@ -44,8 +46,7 @@ template class eoProportionalSelect: public eoSelectOne { public: /// Sanity check - eoProportionalSelect(const eoPop& pop = eoPop()): - total((pop.size() == 0) ? -1.0 : sum_fitness(pop)) + eoProportionalSelect(const eoPop& pop = eoPop()) { if (minimizing_fitness()) throw std::logic_error("eoProportionalSelect: minimizing fitness"); @@ -53,18 +54,32 @@ public: void setup(const eoPop& _pop) { - total = sum_fitness(_pop); + if (_pop.size() == 0) return; + + cumulative.resize(_pop.size()); + cumulative[0] = _pop[0].fitness(); + + for (unsigned i = 1; i < _pop.size(); ++i) + { + cumulative[i] = _pop[i].fitness() + cumulative[i-1]; + } } - /** do the selection, call roulette_wheel. + /** do the selection, */ const EOT& operator()(const eoPop& _pop) { - return roulette_wheel(_pop, total) ; + if (cumulative.size() == 0) setup(_pop); + + double fortune = rng.uniform() * cumulative.back(); + typename FitVec::iterator result = std::upper_bound(cumulative.begin(), cumulative.end(), fortune); + return _pop[result - cumulative.begin()]; } private : - typename EOT::Fitness total; + + typedef std::vector FitVec; + FitVec cumulative; }; #endif diff --git a/eo/src/eoStochasticUniversalSelect.h b/eo/src/eoStochasticUniversalSelect.h new file mode 100755 index 000000000..d9ec16ef7 --- /dev/null +++ b/eo/src/eoStochasticUniversalSelect.h @@ -0,0 +1,102 @@ +// -*- mode: c++; c-indent-level: 4; c++-member-init-indent: 8; comment-column: 35; -*- + +//----------------------------------------------------------------------------- +// eoStochasticUniversalSelect.h +// (c) Maarten Keijzer 2003 +/* + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact: todos@geneura.ugr.es, http://geneura.ugr.es + Marc.Schoenauer@polytechnique.fr + mkeijzer@cs.vu.nl + */ +//----------------------------------------------------------------------------- + +#ifndef eoStochasticUniversalSelect_h +#define eoStochasticUniversalSelect_h + +//----------------------------------------------------------------------------- + +#include +#include + +//----------------------------------------------------------------------------- +/** eoStochasticUniversalSelect: select an individual proportional to her stored fitness + value, but in contrast with eoStochasticUniversalSelect, get rid of most finite sampling effects + by doing all selections in one go, using a single random number. +*/ +//----------------------------------------------------------------------------- + +template class eoStochasticUniversalSelect: public eoSelectOne +{ +public: + /// Sanity check + eoStochasticUniversalSelect(const eoPop& pop = eoPop()) + { + if (minimizing_fitness()) + throw std::logic_error("eoStochasticUniversalSelect: minimizing fitness"); + } + + void setup(const eoPop& _pop) + { + if (_pop.size() == 0) return; + + std::vector cumulative(_pop.size()); + + cumulative[0] = _pop[0].fitness(); + for (unsigned i = 1; i < _pop.size(); ++i) + { + cumulative[i] = _pop[i].fitness() + cumulative[i-1]; + } + + indices.reserve(_pop.size()); + indices.resize(0); + + double fortune = rng.uniform() * cumulative.back(); + double step = cumulative.back() / double(_pop.size()); + + unsigned i = std::upper_bound(cumulative.begin(), cumulative.end(), fortune) - cumulative.begin(); + + while (indices.size() < _pop.size()) { + + while (cumulative[i] < fortune) {i++;} // linear search is good enough as we average one step each time + + indices.push_back(i); + fortune += step; + if (fortune >= cumulative.back()) { // start at the beginning + fortune -= cumulative.back(); + i = 0; + } + } + } + + /** do the selection, + */ + const EOT& operator()(const eoPop& _pop) + { + if (indices.empty()) setup(_pop); + + unsigned index = indices.back(); + indices.pop_back(); + return _pop[index]; + } + +private : + + typedef std::vector IndexVec; + IndexVec indices; +}; + +#endif