OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-cogutil / opencog / util / random.h

145 lines · c ref:
/* random.h ---
 *
 * Copyright (C) 2010 Novamente LLC
 *
 * Author: Nil Geisweiller
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#ifndef _OPENCOG_RANDOM_H
#define _OPENCOG_RANDOM_H

#include <iomanip>
#include <sstream>
#include <limits>
#include <type_traits>

#include <opencog/util/RandGen.h>
#include <opencog/util/mt19937ar.h>
#include <opencog/util/numeric.h>

/**
 * \file random.h
 *
 * This file contains a collection of random generators based on RandGen
 */

namespace opencog {
/** \addtogroup grp_cogutil
 *  @{
 */

//! Pick an element of container c randomly, with uniform
//! distribution.  \warning it is assumed that c is non-empty
template<typename C>
const typename C::value_type& rand_element(const C& c, RandGen& rng=randGen())
{
    OC_ASSERT(!c.empty());
    return *std::next(c.begin(), rng.randint(c.size()));
}

//! Non-const version of above. Pick an element of container c
//! randomly, with uniform distribution.  \warning it is assumed that
//! c is non-empty
template<typename C>
typename C::value_type& rand_element(C& c, RandGen& rng=randGen())
{
    OC_ASSERT(!c.empty());
    return *std::next(c.begin(), rng.randint(c.size()));
}

//! Pick an element of container c randomly, with distribution d.
//! \warning it is assumed that c is non-empty
template<typename C, typename D>
const typename C::value_type& rand_element(const C& c, D& d, RandGen& rng=randGen())
{
    OC_ASSERT(!c.empty());
    return *std::next(c.begin(), d(rng));
}

//! Non-const version of above. Pick an element of container c
//! randomly, with uniform distribution.  \warning it is assumed that
//! c is non-empty
template<typename C, typename D>
typename C::value_type& rand_element(C& c, D& d, RandGen& rng=randGen())
{
    OC_ASSERT(!c.empty());
    return *std::next(c.begin(), d(rng));
}

//! Pick an element of container c randomly, with uniform
//! distribution, and remove it.  \warning it is assumed that c is
//! non-empty
template<typename C>
typename C::value_type rand_element_erase(C& c, RandGen& rng=randGen())
{
    OC_ASSERT(!c.empty());
    auto it = std::next(c.begin(), rng.randint(c.size()));
    typename C::value_type val = *it;
    c.erase(it);
    return val;
}

//! Return a random number sampled according to a Gaussian distribution.
//! If the number falls out of the range of T then it is automatically
//! truncated.
template<typename T>
T gaussian_rand(T mean, T std_dev, RandGen& rng=randGen())
{
    double val = mean + std_dev *
        std::sqrt(-2.0 * std::log(rng.randdouble_one_excluded())) *
        std::cos(2.0 * M_PI * rng.randdouble_one_excluded());

    // Handle overflow/underflow without Boost
    if (std::is_integral<T>::value) {
        // For integral types, clamp to min/max values
        if (val > static_cast<double>(std::numeric_limits<T>::max())) {
            return std::numeric_limits<T>::max();
        } else if (val < static_cast<double>(std::numeric_limits<T>::min())) {
            return std::numeric_limits<T>::min();
        }
    }

    return static_cast<T>(val);
}

//! linear biased random bool, b in [0,1] when b tends to 1 the result
//! tends to be true
static inline bool biased_randbool(float b, RandGen& rng=randGen())
{
    return b > rng.randfloat();
}

//! Generate a random string of characters in the given base, using n
//! random ints, and appending it to a given prefix.
static inline std::string randstr(const std::string& prefix=std::string(),
                                  unsigned n=1, int base=16,
                                  RandGen& rng=randGen())
{
	std::stringstream ss;
	ss << prefix << std::setbase(base);
	for (unsigned j=0; j<n; j++)
		ss << rng.randint();
	return ss.str();
}

/** @}*/
} // ~namespace opencog

#endif // _OPENCOG_RANDOM_H