mirror of
https://github.com/moses-smt/mosesdecoder.git
synced 2024-12-24 04:12:47 +03:00
Add cross-platform randomizer module.
The code uses two mechanisms for generating random numbers: srand()/rand(), which is not thread-safe, and srandom()/random(), which is POSIX-specific. Here I add a util/random.cc module that centralizes these calls, and unifies some common usage patterns. If the implementation is not good enough, we can now change it in a single place. To keep things simple, this uses the portable srand()/rand() but protects them with a lock to avoid concurrency problems. The hard part was to keep the regression tests passing: they rely on fixed sequences of random numbers, so a small code change could break them very thoroughly. Util::rand(), for wide types like size_t, calls std::rand() not once but twice. This behaviour was generalized into utils::wide_rand() and friends.
This commit is contained in:
parent
4b47e1148c
commit
38d790cac0
@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include "moses/Word.h"
|
||||
#include "moses/FF/FeatureFunction.h"
|
||||
#include "Decoder.h"
|
||||
#include "util/random.hh"
|
||||
|
||||
typedef std::map<const Moses::FeatureFunction*, std::vector< float > > ProducerWeightMap;
|
||||
typedef std::pair<const Moses::FeatureFunction*, std::vector< float > > ProducerWeightPair;
|
||||
@ -37,8 +38,11 @@ template <class T> bool from_string(T& t, const std::string& s, std::ios_base& (
|
||||
|
||||
struct RandomIndex {
|
||||
ptrdiff_t operator()(ptrdiff_t max) {
|
||||
srand(time(0)); // Initialize random number generator with current time.
|
||||
return static_cast<ptrdiff_t> (rand() % max);
|
||||
// TODO: Don't seed the randomizer here. If this function gets called
|
||||
// multiple times in the same second, it will return the same value on
|
||||
// each of those calls.
|
||||
util::rand_init();
|
||||
return util::rand_excl(max);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -42,6 +42,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include "RelativeEntropyCalc.h"
|
||||
#include "LexicalReordering.h"
|
||||
#include "LexicalReorderingState.h"
|
||||
#include "util/random.hh"
|
||||
|
||||
#ifdef HAVE_PROTOBUF
|
||||
#include "hypergraph.pb.h"
|
||||
@ -205,7 +206,7 @@ int main(int argc, char** argv)
|
||||
|
||||
|
||||
//initialise random numbers
|
||||
srand(time(NULL));
|
||||
rand_init();
|
||||
|
||||
// set up read/writing class
|
||||
IOWrapper* ioWrapper = GetIOWrapper(staticData);
|
||||
|
@ -287,7 +287,7 @@ void Data::createShards(size_t shard_count, float shard_size, const string& scor
|
||||
} else {
|
||||
//create shards by randomly sampling
|
||||
for (size_t i = 0; i < floor(shard_size+0.5); ++i) {
|
||||
shard_contents.push_back(util::rand_int() % data_size);
|
||||
shard_contents.push_back(util::rand_excl(data_size));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,10 +58,8 @@ void Point::Randomize()
|
||||
UTIL_THROW_IF(m_min.size() != Point::m_dim, util::Exception, "Error");
|
||||
UTIL_THROW_IF(m_max.size() != Point::m_dim, util::Exception, "Error");
|
||||
|
||||
for (unsigned int i = 0; i < size(); i++) {
|
||||
const float scale = (m_max[i] - m_min[i]) / float(RAND_MAX);
|
||||
operator[](i) = m_min[i] + util::rand_int() * scale;
|
||||
}
|
||||
for (unsigned int i = 0; i < size(); i++)
|
||||
operator[](i) = util::rand_incl(m_min[i], m_max[i]);
|
||||
}
|
||||
|
||||
double Point::operator*(const FeatureStats& F) const
|
||||
|
@ -5,11 +5,8 @@
|
||||
|
||||
- check that --pairwise-ranked is compatible with all optimization metrics
|
||||
|
||||
- Replace the standard rand() currently used in MERT and PRO with better
|
||||
random generators such as Boost's random generators (e.g., boost::mt19937).
|
||||
- create a Random class to hide the details, i.e., how to generate
|
||||
random numbers, which allows us to use custom random generators more
|
||||
easily.
|
||||
- Use better random generators in util/random.cc, e.g. boost::mt19937.
|
||||
- Support plugging of custom random generators.
|
||||
|
||||
Pros:
|
||||
- In MERT, you might want to use the random restarting technique to avoid
|
||||
|
@ -95,7 +95,7 @@ void EvaluatorUtil::evaluate(const string& candFile, int bootstrap, bool nbest_i
|
||||
for (int i = 0; i < bootstrap; ++i) {
|
||||
ScoreData scoredata(g_scorer);
|
||||
for (int j = 0; j < n; ++j) {
|
||||
int randomIndex = util::rand_int() % n;
|
||||
const int randomIndex = util::rand_excl(n);
|
||||
scoredata.add(entries[randomIndex], j);
|
||||
}
|
||||
g_scorer->setScoreData(&scoredata);
|
||||
@ -285,10 +285,10 @@ void InitSeed(const ProgramOption *opt)
|
||||
{
|
||||
if (opt->has_seed) {
|
||||
cerr << "Seeding random numbers with " << opt->seed << endl;
|
||||
util::rand_int_init(opt->seed);
|
||||
util::rand_init(opt->seed);
|
||||
} else {
|
||||
cerr << "Seeding random numbers with system clock " << endl;
|
||||
util::rand_int_init();
|
||||
util::rand_init();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ de recherches du Canada
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include "util/exception.hh"
|
||||
#include "util/random.hh"
|
||||
|
||||
#include "BleuScorer.h"
|
||||
#include "HopeFearDecoder.h"
|
||||
@ -122,10 +123,10 @@ int main(int argc, char** argv)
|
||||
|
||||
if (vm.count("random-seed")) {
|
||||
cerr << "Initialising random seed to " << seed << endl;
|
||||
srand(seed);
|
||||
util::rand_init(seed);
|
||||
} else {
|
||||
cerr << "Initialising random seed from system clock" << endl;
|
||||
srand(time(NULL));
|
||||
util::rand_init();
|
||||
}
|
||||
|
||||
// Initialize weights
|
||||
|
@ -290,10 +290,10 @@ int main(int argc, char **argv)
|
||||
|
||||
if (option.has_seed) {
|
||||
cerr << "Seeding random numbers with " << option.seed << endl;
|
||||
util::rand_int_init(option.seed);
|
||||
util::rand_init(option.seed);
|
||||
} else {
|
||||
cerr << "Seeding random numbers with system clock " << endl;
|
||||
util::rand_int_init();
|
||||
util::rand_init();
|
||||
}
|
||||
|
||||
if (option.sparse_weights_file.size()) ++option.pdim;
|
||||
|
@ -43,6 +43,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include "ScoreDataIterator.h"
|
||||
#include "BleuScorer.h"
|
||||
#include "Util.h"
|
||||
#include "util/random.hh"
|
||||
|
||||
using namespace std;
|
||||
using namespace MosesTuning;
|
||||
@ -141,10 +142,10 @@ int main(int argc, char** argv)
|
||||
|
||||
if (vm.count("random-seed")) {
|
||||
cerr << "Initialising random seed to " << seed << endl;
|
||||
srand(seed);
|
||||
util::rand_init(seed);
|
||||
} else {
|
||||
cerr << "Initialising random seed from system clock" << endl;
|
||||
srand(time(NULL));
|
||||
util::rand_init();
|
||||
}
|
||||
|
||||
if (scoreFiles.size() == 0 || featureFiles.size() == 0) {
|
||||
@ -211,11 +212,11 @@ int main(int argc, char** argv)
|
||||
vector<float> scores;
|
||||
size_t n_translations = hypotheses.size();
|
||||
for(size_t i=0; i<n_candidates; i++) {
|
||||
size_t rand1 = rand() % n_translations;
|
||||
size_t rand1 = util::rand_excl(n_translations);
|
||||
pair<size_t,size_t> translation1 = hypotheses[rand1];
|
||||
float bleu1 = smoothedSentenceBleu(scoreDataIters[translation1.first]->operator[](translation1.second), bleuSmoothing, smoothBP);
|
||||
|
||||
size_t rand2 = rand() % n_translations;
|
||||
size_t rand2 = util::rand_excl(n_translations);
|
||||
pair<size_t,size_t> translation2 = hypotheses[rand2];
|
||||
float bleu2 = smoothedSentenceBleu(scoreDataIters[translation2.first]->operator[](translation2.second), bleuSmoothing, smoothBP);
|
||||
|
||||
|
@ -45,6 +45,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include "moses/FF/StatefulFeatureFunction.h"
|
||||
#include "moses/FF/StatelessFeatureFunction.h"
|
||||
#include "moses/TrainingTask.h"
|
||||
#include "util/random.hh"
|
||||
|
||||
#ifdef HAVE_PROTOBUF
|
||||
#include "hypergraph.pb.h"
|
||||
@ -117,7 +118,7 @@ int main(int argc, char** argv)
|
||||
|
||||
|
||||
//initialise random numbers
|
||||
srand(time(NULL));
|
||||
util::rand_init();
|
||||
|
||||
// set up read/writing class
|
||||
IFVERBOSE(1) {
|
||||
|
@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "util/random.hh"
|
||||
#include "util/usage.hh"
|
||||
|
||||
#ifdef WIN32
|
||||
@ -91,7 +92,7 @@ SimpleTranslationInterface::SimpleTranslationInterface(const string &mosesIni):
|
||||
exit(1);
|
||||
}
|
||||
|
||||
srand(time(NULL));
|
||||
util::rand_init();
|
||||
|
||||
}
|
||||
|
||||
@ -185,7 +186,7 @@ batch_run()
|
||||
const StaticData& staticData = StaticData::Instance();
|
||||
|
||||
//initialise random numbers
|
||||
srand(time(NULL));
|
||||
util::rand_init();
|
||||
|
||||
IFVERBOSE(1) PrintUserTime("Created input-output object");
|
||||
|
||||
|
@ -54,6 +54,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#endif
|
||||
|
||||
#include "util/exception.hh"
|
||||
#include "util/random.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -418,7 +419,7 @@ void Manager::CalcLatticeSamples(size_t count, TrellisPathList &ret) const
|
||||
//cerr << endl;
|
||||
|
||||
//draw the sample
|
||||
float frandom = log((float)rand()/RAND_MAX);
|
||||
const float frandom = log(util::rand_incl(0.0f, 1.0f));
|
||||
size_t position = 1;
|
||||
float sum = candidateScores[0];
|
||||
for (; position < candidateScores.size() && sum < frandom; ++position) {
|
||||
|
@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include "InputFileStream.h"
|
||||
#include "StaticData.h"
|
||||
#include "util/exception.hh"
|
||||
#include "util/random.hh"
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
|
||||
@ -1392,7 +1393,7 @@ struct Credit {
|
||||
this->contact = contact ;
|
||||
this->currentPursuits = currentPursuits ;
|
||||
this->areaResponsibility = areaResponsibility;
|
||||
this->sortId = rand() % 1000;
|
||||
this->sortId = util::rand_excl(1000);
|
||||
}
|
||||
|
||||
bool operator<(const Credit &other) const {
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "utils.h"
|
||||
#include "FileHandler.h"
|
||||
#include "util/exception.hh"
|
||||
#include "util/random.hh"
|
||||
|
||||
using namespace Moses;
|
||||
typedef uint64_t P; // largest input range is 2^64
|
||||
@ -162,7 +163,7 @@ void Hash_shiftAddXOR<T>::initSeeds()
|
||||
{
|
||||
v_ = new T[this->H_];
|
||||
for(count_t i=0; i < this->H_; i++)
|
||||
v_[i] = Utils::rand<T>() + 1;
|
||||
v_[i] = util::wide_rand<T>() + 1;
|
||||
}
|
||||
template <typename T>
|
||||
T Hash_shiftAddXOR<T>::hash(const char* s, count_t h)
|
||||
@ -187,9 +188,8 @@ void UnivHash_tableXOR<T>::initSeeds()
|
||||
// fill with random values
|
||||
for(count_t j=0; j < this->H_; j++) {
|
||||
table_[j] = new T[tblLen_];
|
||||
for(count_t i=0; i < tblLen_; i++) {
|
||||
table_[j][i] = Utils::rand<T>(this->m_-1);
|
||||
}
|
||||
for(count_t i=0; i < tblLen_; i++)
|
||||
table_[j][i] = util::wide_rand_excl(this->m_-1);
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
@ -218,7 +218,7 @@ void UnivHash_noPrimes<T>::initSeeds()
|
||||
{
|
||||
a_ = new P[this->H_];
|
||||
for(T i=0; i < this->H_; i++) {
|
||||
a_[i] = Utils::rand<P>();
|
||||
a_[i] = util::wide_rand<P>();
|
||||
if(a_[i] % 2 == 0) a_[i]++; // a must be odd
|
||||
}
|
||||
}
|
||||
@ -284,8 +284,8 @@ void UnivHash_linear<T>::initSeeds()
|
||||
a_[i] = new T[MAX_NGRAM_ORDER];
|
||||
b_[i] = new T[MAX_NGRAM_ORDER];
|
||||
for(count_t j=0; j < MAX_NGRAM_ORDER; j++) {
|
||||
a_[i][j] = 1 + Utils::rand<T>();
|
||||
b_[i][j] = Utils::rand<T>();
|
||||
a_[i][j] = 1 + util::wide_rand<T>();
|
||||
b_[i][j] = util::wide_rand<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,22 +62,6 @@ public:
|
||||
str[i] = tolower(str[i]);
|
||||
}
|
||||
}
|
||||
// TODO: interface with decent PRG
|
||||
template<typename T>
|
||||
static T rand(T mod_bnd = 0) {
|
||||
T random = 0;
|
||||
if(sizeof(T) <= 4) {
|
||||
random = static_cast<T>(std::rand());
|
||||
} else if(sizeof(T) == 8) {
|
||||
random = static_cast<T>(std::rand());
|
||||
random <<= 31;
|
||||
random <<= 1;
|
||||
random |= static_cast<T>(std::rand());
|
||||
}
|
||||
if(mod_bnd != 0)
|
||||
return random % mod_bnd;
|
||||
else return random;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "DynSuffixArray.h"
|
||||
#include "util/random.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
@ -315,33 +317,31 @@ int DynSuffixArray::Compare(int pos1, int pos2, int max)
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/// Helper: swap two entries in an int array.
|
||||
inline void swap_ints(int array[], int one, int other)
|
||||
{
|
||||
const int tmp = array[one];
|
||||
array[one] = array[other];
|
||||
array[other] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void DynSuffixArray::Qsort(int* array, int begin, int end)
|
||||
{
|
||||
if(end > begin) {
|
||||
int index;
|
||||
int index = util::rand_incl(begin, end);
|
||||
{
|
||||
index = begin + (rand() % (end - begin + 1));
|
||||
int pivot = array[index];
|
||||
{
|
||||
int tmp = array[index];
|
||||
array[index] = array[end];
|
||||
array[end] = tmp;
|
||||
}
|
||||
const int pivot = array[index];
|
||||
swap_ints(array, index, end);
|
||||
for(int i=index=begin; i < end; ++i) {
|
||||
if (Compare(array[i], pivot, 20) <= 0) {
|
||||
{
|
||||
int tmp = array[index];
|
||||
array[index] = array[i];
|
||||
array[i] = tmp;
|
||||
index++;
|
||||
}
|
||||
swap_ints(array, index, i);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
{
|
||||
int tmp = array[index];
|
||||
array[index] = array[end];
|
||||
array[end] = tmp;
|
||||
}
|
||||
swap_ints(array, index, end);
|
||||
}
|
||||
Qsort(array, begin, index - 1);
|
||||
Qsort(array, index + 1, end);
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "moses/TranslationModel/fuzzy-match/SentenceAlignment.h"
|
||||
#include "util/file.hh"
|
||||
#include "util/exception.hh"
|
||||
#include "util/random.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -62,8 +63,8 @@ char *mkdtemp(char *tempbuf)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
srand((unsigned)time(0));
|
||||
rand_value = (int)((rand() / ((double)RAND_MAX+1.0)) * 1e6);
|
||||
util::rand_init();
|
||||
rand_value = rand_excl(1e6);
|
||||
tempbase = strrchr(tempbuf, '/');
|
||||
tempbase = tempbase ? tempbase+1 : tempbuf;
|
||||
strcpy(tempbasebuf, tempbase);
|
||||
|
@ -2,19 +2,16 @@
|
||||
#define __sampling_h
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "util/random.hh"
|
||||
|
||||
// Utility functions for proper sub-sampling.
|
||||
// (c) 2007-2012 Ulrich Germann
|
||||
|
||||
|
||||
namespace Moses
|
||||
{
|
||||
using namespace std;
|
||||
inline
|
||||
size_t
|
||||
randInt(size_t N)
|
||||
{
|
||||
return N*(rand()/(RAND_MAX+1.));
|
||||
}
|
||||
using namespace std;
|
||||
|
||||
// select a random sample of size /s/ without restitution from the range of
|
||||
// integers [0,N);
|
||||
@ -35,15 +32,15 @@ randomSample(vector<idx_t>& v, size_t s, size_t N)
|
||||
if (s*10<N) {
|
||||
boost::dynamic_bitset<uint64_t> check(N,0);
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
size_t x = randInt(N);
|
||||
while (check[x]) x = randInt(N);
|
||||
size_t x = util::rand_excl(N);
|
||||
while (check[x]) x = util::rand_excl(N);
|
||||
check[x]=true;
|
||||
v[i] = x;
|
||||
}
|
||||
} else {
|
||||
size_t m=0;
|
||||
for (size_t t = 0; m <= s && t < N; t++)
|
||||
if (s==N || randInt(N-t) < s-m) v[m++] = t;
|
||||
if (s==N || util::rand_excl(N-t) < s-m) v[m++] = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,7 +345,7 @@
|
||||
// {
|
||||
// boost::lock_guard<boost::mutex> lock(stats->lock);
|
||||
// if (stats->raw_cnt == ctr) ++stats->raw_cnt;
|
||||
// size_t rnum = randInt(stats->raw_cnt - ctr++);
|
||||
// size_t rnum = util::rand_excl(stats->raw_cnt - ctr++);
|
||||
// // cout << stats->raw_cnt << " " << ctr-1 << " "
|
||||
// // << rnum << " " << max_samples - stats->good << endl;
|
||||
// if (rnum < max_samples - stats->good)
|
||||
|
@ -69,7 +69,7 @@ namespace ugdiss
|
||||
// while (chosen < samplesize && next < stop)
|
||||
// {
|
||||
// root->readEntry(next,*this);
|
||||
// if (randInt(N - sampled++) < samplesize - chosen)
|
||||
// if (util::rand_excl(N - sampled++) < samplesize - chosen)
|
||||
// {
|
||||
// ++chosen;
|
||||
// return true;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <iostream>
|
||||
#include "util/exception.hh"
|
||||
#include "moses/Util.h"
|
||||
#include "util/random.hh"
|
||||
//#include <cassert>
|
||||
|
||||
// #include "ug_bv_iter.h"
|
||||
@ -894,13 +895,6 @@ namespace ugdiss
|
||||
return bv;
|
||||
}
|
||||
|
||||
inline
|
||||
size_t
|
||||
randInt(size_t N)
|
||||
{
|
||||
return size_t(N*(rand()/(RAND_MAX+1.)));
|
||||
}
|
||||
|
||||
/// randomly select up to N occurrences of the sequence
|
||||
template<typename Token>
|
||||
sptr<vector<typename ttrack::Position> >
|
||||
@ -922,8 +916,8 @@ namespace ugdiss
|
||||
root->readEntry(I.next,I);
|
||||
|
||||
// t: expected number of remaining samples
|
||||
double t = (stop - I.pos)/root->aveIndexEntrySize();
|
||||
double r = t*rand()/(RAND_MAX+1.);
|
||||
const double t = (stop - I.pos)/root->aveIndexEntrySize();
|
||||
const double r = util::rand_excl(t);
|
||||
if (r < N-m)
|
||||
{
|
||||
ret->at(m).offset = I.offset;
|
||||
|
@ -19,21 +19,25 @@ namespace
|
||||
boost::mutex rand_lock;
|
||||
} // namespace
|
||||
|
||||
void rand_int_init(unsigned int seed)
|
||||
void rand_init(unsigned int seed)
|
||||
{
|
||||
boost::lock_guard<boost::mutex> lock(rand_lock);
|
||||
srand(seed);
|
||||
}
|
||||
|
||||
|
||||
void rand_int_init()
|
||||
void rand_init()
|
||||
{
|
||||
rand_int_init(time(NULL));
|
||||
rand_init(time(NULL));
|
||||
}
|
||||
|
||||
namespace internal
|
||||
{
|
||||
// This is the one call to the actual randomizer. All else is built on this.
|
||||
int rand_int()
|
||||
{
|
||||
boost::lock_guard<boost::mutex> lock(rand_lock);
|
||||
return rand();
|
||||
return std::rand();
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace util
|
||||
|
207
util/random.hh
207
util/random.hh
@ -1,32 +1,229 @@
|
||||
#ifndef UTIL_RANDOM_H
|
||||
#define UTIL_RANDOM_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
|
||||
namespace util
|
||||
{
|
||||
/** Thread-safe, cross-platform random number generator.
|
||||
*
|
||||
* This is not for proper security-grade randomness, but should be "good
|
||||
* enough" for producing arbitrary values of various numeric types.
|
||||
*
|
||||
* Before starting, call rand_init() to seed the randomizer. There is no need
|
||||
* to do this more than once; in fact doing it more often is likely to make the
|
||||
* randomizer less effective. Once that is done, call the rand(), rand_excl(),
|
||||
* and rand_incl() functions as needed to generate pseudo-random numbers.
|
||||
*
|
||||
* Probability distribution is roughly uniform, but for integral types is
|
||||
* skewed slightly towards lower numbers depending on how close "top" comes to
|
||||
* RAND_MAX.
|
||||
*
|
||||
* For floating-point types, resolution is limited; there will actually be
|
||||
* only RAND_MAX different possible values.
|
||||
*/
|
||||
|
||||
/** Initialize randomizer with a fixed seed.
|
||||
*
|
||||
* After this, unless the randomizer gets seeded again, consecutive calls to
|
||||
* rand_int() will return a sequence of pseudo-random numbers determined by
|
||||
* the seed. Every time the randomizer is seeded with this same seed, it will
|
||||
* again start returning the same sequence of numbers.
|
||||
* the random functions will return a sequence of pseudo-random numbers
|
||||
* determined by the seed. Every time the randomizer is seeded with this same
|
||||
* seed, it will again start returning the same sequence of numbers.
|
||||
*/
|
||||
void rand_int_init(unsigned int);
|
||||
void rand_init(unsigned int);
|
||||
|
||||
/** Initialize randomizer based on current time.
|
||||
*
|
||||
* Call this to make the randomizer return hard-to-predict numbers. It won't
|
||||
* produce high-grade randomness, but enough to make the program act
|
||||
* differently on different runs.
|
||||
*
|
||||
* The seed will be based on the current time in seconds. So calling it twice
|
||||
* within the same second will just reset the randomizer to where it was before.
|
||||
* Don't do that.
|
||||
*/
|
||||
void rand_int_init();
|
||||
void rand_init();
|
||||
|
||||
|
||||
/** Return a pseudorandom number between 0 and RAND_MAX inclusive.
|
||||
*
|
||||
* Initialize (seed) the randomizer before starting to call this.
|
||||
*/
|
||||
template<typename T> inline T rand();
|
||||
|
||||
|
||||
/** Return a pseudorandom number in the half-open interval [bottom, top).
|
||||
*
|
||||
* Generates a value between "bottom" (inclusive) and "top" (exclusive),
|
||||
* assuming that (top - bottom) <= RAND_MAX.
|
||||
*/
|
||||
template<typename T> inline T rand_excl(T bottom, T top);
|
||||
|
||||
|
||||
/** Return a pseudorandom number in the half-open interval [0, top).
|
||||
*
|
||||
* Generates a value between 0 (inclusive) and "top" (exclusive), assuming that
|
||||
* bottom <= RAND_MAX.
|
||||
*/
|
||||
template<typename T> inline T rand_excl(T top);
|
||||
|
||||
|
||||
/** Return a pseudorandom number in the open interval [bottom, top].
|
||||
*
|
||||
* Generates a value between "bottom" and "top" inclusive, assuming that
|
||||
* (top - bottom) < RAND_MAX.
|
||||
*/
|
||||
template<typename T> inline T rand_incl(T bottom, T top);
|
||||
|
||||
|
||||
/** Return a pseudorandom number in the open interval [0, top].
|
||||
*
|
||||
* Generates a value between 0 and "top" inclusive, assuming that
|
||||
* bottom < RAND_MAX.
|
||||
*/
|
||||
template<typename T> inline T rand_incl(T top);
|
||||
|
||||
|
||||
/** Return a pseudorandom number which may be larger than RAND_MAX.
|
||||
*
|
||||
* The requested type must be integral, and its size must be an even multiple
|
||||
* of the size of an int. The return value will combine one or more random
|
||||
* ints into a single value, which could get quite large.
|
||||
*
|
||||
* The result is nonnegative. Because the constituent ints are also
|
||||
* nonnegative, the most significant bit in each of the ints will be zero,
|
||||
* so for a wider type, there will be "gaps" in the range of possible outputs.
|
||||
*/
|
||||
template<typename T> inline T wide_rand();
|
||||
|
||||
/** Return a pseudorandom number in [0, top), not limited to RAND_MAX.
|
||||
*
|
||||
* Works like wide_rand(), but if the requested type is wider than an int, it
|
||||
* accommodates larger top values than an int can represent.
|
||||
*/
|
||||
template<typename T> inline T wide_rand_excl(T top);
|
||||
|
||||
/** Return a pseudorandom number in [bottom, top), not limited to RAND_MAX.
|
||||
*
|
||||
* Works like wide_rand(), but if the requested type is wider than an int, it
|
||||
* accommodates larger value ranges than an int can represent.
|
||||
*/
|
||||
template<typename T> inline T wide_rand_excl(T bottom, T top);
|
||||
|
||||
/** Return a pseudorandom number in [0, top], not limited to RAND_MAX.
|
||||
*
|
||||
* Works like wide_rand(), but if the requested type is wider than an int, it
|
||||
* accommodates larger top values than an int can represent.
|
||||
*/
|
||||
template<typename T> inline T wide_rand_incl(T top);
|
||||
|
||||
/** Return a pseudorandom number in [bottom, top], not limited to RAND_MAX.
|
||||
*
|
||||
* Works like wide_rand(), but if the requested type is wider than an int, it
|
||||
* accommodates larger top values than an int can represent.
|
||||
*/
|
||||
template<typename T> inline T wide_rand_incl(T bottom, T top);
|
||||
|
||||
|
||||
/// Implementation detail. For the random module's internal use only.
|
||||
namespace internal
|
||||
{
|
||||
/// The central call to the randomizer upon which this whole module is built.
|
||||
int rand_int();
|
||||
|
||||
/// Helper template: customize random values to required ranges.
|
||||
template<typename T, bool is_integer_type> struct random_scaler;
|
||||
|
||||
/// Specialized random_scaler for integral types.
|
||||
template<typename T> struct random_scaler<T, true>
|
||||
{
|
||||
static T rnd_excl(T value, T range) { return value % range; }
|
||||
static T rnd_incl(T value, T range) { return value % (range + 1); }
|
||||
};
|
||||
|
||||
/// Specialized random_scaler for non-integral types.
|
||||
template<typename T> struct random_scaler<T, false>
|
||||
{
|
||||
static T rnd_excl(T value, T range)
|
||||
{
|
||||
// Promote RAND_MAX to T before adding one to avoid overflow.
|
||||
return range * value / (T(RAND_MAX) + 1);
|
||||
}
|
||||
static T rnd_incl(T value, T range) { return range * value / RAND_MAX; }
|
||||
};
|
||||
|
||||
/// Helper for filling a wider variable with random ints.
|
||||
template<typename T, size_t remaining_ints> struct wide_random_collector
|
||||
{
|
||||
static T generate()
|
||||
{
|
||||
T one_int = util::rand<T>() << (8 * sizeof(int));
|
||||
return one_int | wide_random_collector<T, remaining_ints-1>::generate();
|
||||
}
|
||||
};
|
||||
/// Specialized wide_random_collector for generating just a single int.
|
||||
template<typename T> struct wide_random_collector<T, 1>
|
||||
{
|
||||
static T generate() { return util::rand<T>(); }
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
|
||||
template<typename T> inline T rand()
|
||||
{
|
||||
return T(util::internal::rand_int());
|
||||
}
|
||||
|
||||
template<typename T> inline T rand_excl(T top)
|
||||
{
|
||||
typedef internal::random_scaler<T, std::numeric_limits<T>::is_integer> scaler;
|
||||
return scaler::rnd_excl(util::rand<T>(), top);
|
||||
}
|
||||
|
||||
template<typename T> inline T rand_excl(T bottom, T top)
|
||||
{
|
||||
return bottom + rand_excl(top - bottom);
|
||||
}
|
||||
|
||||
template<typename T> inline T rand_incl(T top)
|
||||
{
|
||||
typedef internal::random_scaler<T, std::numeric_limits<T>::is_integer> scaler;
|
||||
return scaler::rnd_incl(util::rand<T>(), top);
|
||||
}
|
||||
|
||||
template<typename T> inline T rand_incl(T bottom, T top)
|
||||
{
|
||||
return bottom + rand_incl(top - bottom);
|
||||
}
|
||||
|
||||
template<typename T> inline T wide_rand()
|
||||
{
|
||||
return internal::wide_random_collector<T, sizeof(T)/sizeof(int)>::generate();
|
||||
}
|
||||
|
||||
template<typename T> inline T wide_rand_excl(T top)
|
||||
{
|
||||
typedef internal::random_scaler<T, std::numeric_limits<T>::is_integer> scaler;
|
||||
return scaler::rnd_excl(util::wide_rand<T>(), top);
|
||||
}
|
||||
|
||||
template<typename T> inline T wide_rand_excl(T bottom, T top)
|
||||
{
|
||||
return bottom + wide_rand_excl(top - bottom);
|
||||
}
|
||||
|
||||
template<typename T> inline T wide_rand_incl(T top)
|
||||
{
|
||||
typedef internal::random_scaler<T, std::numeric_limits<T>::is_integer> scaler;
|
||||
return scaler::rnd_incl(util::wide_rand<T>(), top);
|
||||
}
|
||||
|
||||
template<typename T> inline T wide_rand_incl(T bottom, T top)
|
||||
{
|
||||
return bottom + wide_rand_incl(top - bottom);
|
||||
}
|
||||
} // namespace util
|
||||
|
||||
#endif
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <cstdlib>
|
||||
|
||||
#include "util/random.hh"
|
||||
|
||||
#define BOOST_TEST_MODULE RandomTest
|
||||
@ -8,32 +10,182 @@ namespace util
|
||||
namespace
|
||||
{
|
||||
|
||||
BOOST_AUTO_TEST_CASE(returns_different_consecutive_numbers)
|
||||
BOOST_AUTO_TEST_CASE(rand_int_returns_positive_no_greater_than_RAND_MAX)
|
||||
{
|
||||
rand_int_init(99);
|
||||
const int first = rand_int(), second = rand_int(), third = rand_int();
|
||||
rand_init();
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
const int random_number = rand<int>();
|
||||
BOOST_CHECK(random_number >= 0);
|
||||
BOOST_CHECK(random_number <= RAND_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rand_int_returns_different_consecutive_numbers)
|
||||
{
|
||||
rand_init(99);
|
||||
const int first = rand<int>(), second = rand<int>(), third = rand<int>();
|
||||
// Sometimes you'll get the same number twice in a row, but generally the
|
||||
// randomizer returns different numbers.
|
||||
BOOST_CHECK(second != first || third != first);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(returns_different_numbers_for_different_seeds)
|
||||
BOOST_AUTO_TEST_CASE(rand_int_returns_different_numbers_for_different_seeds)
|
||||
{
|
||||
rand_int_init(1);
|
||||
const int one1 = rand_int(), one2 = rand_int();
|
||||
rand_int_init(2);
|
||||
const int two1 = rand_int(), two2 = rand_int();
|
||||
BOOST_CHECK(two1 != one1 || two2 != two1);
|
||||
rand_init(1);
|
||||
const int one1 = rand<int>(), one2 = rand<int>();
|
||||
rand_init(2);
|
||||
const int two1 = rand<int>(), two2 = rand<int>();
|
||||
BOOST_CHECK(two1 != one1 || two2 != one2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(returns_same_sequence_for_same_seed)
|
||||
BOOST_AUTO_TEST_CASE(rand_int_returns_same_sequence_for_same_seed)
|
||||
{
|
||||
rand_int_init(1);
|
||||
const int first = rand_int();
|
||||
rand_int_init(1);
|
||||
const int second = rand_int();
|
||||
rand_init(1);
|
||||
const int first = rand<int>();
|
||||
rand_init(1);
|
||||
const int second = rand<int>();
|
||||
BOOST_CHECK_EQUAL(first, second);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rand_excl_int_returns_number_in_range)
|
||||
{
|
||||
const int bottom = 10, top = 50;
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
const int random_number = rand_excl(bottom, top);
|
||||
BOOST_CHECK(random_number >= bottom);
|
||||
BOOST_CHECK(random_number < top);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rand_excl_int_covers_full_range)
|
||||
{
|
||||
// The spread of random numbers really goes all the way from 0 (inclusive)
|
||||
// to "top" (exclusive). It's not some smaller subset.
|
||||
// This test will randomly fail sometimes, though very very rarely, when the
|
||||
// random numbers don't actually have enough different values.
|
||||
const int bottom = 1, top = 4;
|
||||
int lowest = 99, highest = -1;
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
const int random_number = rand_excl(bottom, top);
|
||||
lowest = std::min(lowest, random_number);
|
||||
highest = std::max(highest, random_number);
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(lowest, bottom);
|
||||
BOOST_CHECK_EQUAL(highest, top - 1);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rand_incl_int_returns_number_in_range)
|
||||
{
|
||||
const int bottom = 10, top = 50;
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
const int random_number = rand_incl(bottom, top);
|
||||
BOOST_CHECK(random_number >= 0);
|
||||
BOOST_CHECK(random_number <= top);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rand_incl_int_covers_full_range)
|
||||
{
|
||||
// The spread of random numbers really goes all the way from 0 to "top"
|
||||
// inclusive. It's not some smaller subset.
|
||||
// This test will randomly fail sometimes, though very very rarely, when the
|
||||
// random numbers don't actually have enough different values.
|
||||
const int bottom = 1, top = 4;
|
||||
int lowest = 99, highest = -1;
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
const int random_number = rand_incl(bottom, top);
|
||||
lowest = std::min(lowest, random_number);
|
||||
highest = std::max(highest, random_number);
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(lowest, bottom);
|
||||
BOOST_CHECK_EQUAL(highest, top);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rand_excl_float_returns_float_in_range)
|
||||
{
|
||||
const float bottom = 5, top = 10;
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
const float random_number = rand_excl(bottom, top);
|
||||
BOOST_CHECK(random_number >= bottom);
|
||||
BOOST_CHECK(random_number < top);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rand_excl_float_returns_different_values)
|
||||
{
|
||||
const float bottom = 5, top = 10;
|
||||
float lowest = 99, highest = -1;
|
||||
for (int i=0; i<10; i++)
|
||||
{
|
||||
const float random_number = rand_excl(bottom, top);
|
||||
lowest = std::min(lowest, random_number);
|
||||
highest = std::max(highest, random_number);
|
||||
}
|
||||
BOOST_CHECK(lowest < highest);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rand_float_incl_returns_float_in_range)
|
||||
{
|
||||
const float bottom = 5, top = 10;
|
||||
for (int i=0; i<1000; i++)
|
||||
{
|
||||
const float random_number = rand_excl(bottom, top);
|
||||
BOOST_CHECK(random_number >= bottom);
|
||||
BOOST_CHECK(random_number <= top);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(rand_float_incl_returns_different_values)
|
||||
{
|
||||
const float bottom = 0, top = 10;
|
||||
float lowest = 99, highest = -1;
|
||||
for (int i=0; i<10; i++)
|
||||
{
|
||||
const float random_number = rand_excl(bottom, top);
|
||||
lowest = std::min(lowest, random_number);
|
||||
highest = std::max(highest, random_number);
|
||||
}
|
||||
BOOST_CHECK(lowest < highest);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wide_rand_int_returns_different_numbers_in_range)
|
||||
{
|
||||
for (int i=0; i<100; i++)
|
||||
{
|
||||
const int random_number = wide_rand<int>();
|
||||
BOOST_CHECK(random_number >= 0);
|
||||
BOOST_CHECK(random_number <= RAND_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wide_rand_long_long_returns_big_numbers)
|
||||
{
|
||||
long long one = wide_rand<long long>(), two = wide_rand<long long>();
|
||||
// This test will fail sometimes because of unlucky random numbers, but only
|
||||
// very very rarely.
|
||||
BOOST_CHECK(one > RAND_MAX || two > RAND_MAX);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(wide_rand_excl_supports_larger_range)
|
||||
{
|
||||
const long long top = 1000 * (long long)RAND_MAX;
|
||||
long long
|
||||
one = wide_rand_excl<long long>(top),
|
||||
two = wide_rand_excl<long long>(top);
|
||||
BOOST_CHECK(one < top);
|
||||
BOOST_CHECK(two < top);
|
||||
// This test will fail sometimes because of unlucky random numbers, but only
|
||||
// very very rarely.
|
||||
BOOST_CHECK(one > RAND_MAX || two > RAND_MAX);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace util
|
||||
|
Loading…
Reference in New Issue
Block a user