How to generate Zipf distributed numbers efficiently? - c++

I'm currently benchmarking some data structures in C++ and I want to test them when working on Zipf-distributed numbers.
I'm using the generator provided on this site: http://www.cse.usf.edu/~christen/tools/toolpage.html
I adapted the implementation to use a Mersenne Twister generator.
It works well but it is really slow. In my case, the range can be big (about a million) and the number of random numbers generate can be several millions.
The alpha parameter does not change over time, it is fixed.
I tried to precaculate all the sum_prob. It's much faster, but still slows on big range.
Is there a faster way to generate Zipf distributed numbers ? Even something less precise will be welcome.
Thanks

The pre-calculation alone does not help so much. But as it's obvious the sum_prob is accumulative and has ascending order. So if we use a binary-search to find the zipf_value we would decrease the order of generating a Zipf distributed number from O(n) to O(log(n)). Which is so much improvement in efficiency.Here it is, just replace the zipf() function in genzipf.c with following one:
int zipf(double alpha, int n)
{
static int first = TRUE; // Static first time flag
static double c = 0; // Normalization constant
static double *sum_probs; // Pre-calculated sum of probabilities
double z; // Uniform random number (0 < z < 1)
int zipf_value; // Computed exponential value to be returned
int i; // Loop counter
int low, high, mid; // Binary-search bounds
// Compute normalization constant on first call only
if (first == TRUE)
{
for (i=1; i<=n; i++)
c = c + (1.0 / pow((double) i, alpha));
c = 1.0 / c;
sum_probs = malloc((n+1)*sizeof(*sum_probs));
sum_probs[0] = 0;
for (i=1; i<=n; i++) {
sum_probs[i] = sum_probs[i-1] + c / pow((double) i, alpha);
}
first = FALSE;
}
// Pull a uniform random number (0 < z < 1)
do
{
z = rand_val(0);
}
while ((z == 0) || (z == 1));
// Map z to the value
low = 1, high = n, mid;
do {
mid = floor((low+high)/2);
if (sum_probs[mid] >= z && sum_probs[mid-1] < z) {
zipf_value = mid;
break;
} else if (sum_probs[mid] >= z) {
high = mid-1;
} else {
low = mid+1;
}
} while (low <= high);
// Assert that zipf_value is between 1 and N
assert((zipf_value >=1) && (zipf_value <= n));
return(zipf_value);
}

The only C++11 Zipf random generator I could find calculated the probabilities explicitly and used std::discrete_distribution. This works fine for small ranges, but is not useful if you need to generate Zipf values with a very wide range (for database testing, in my case) since it will exhaust memory. So, I implemented the below-mentioned algorithm in C++.
I have not rigorously tested this code, and some optimizations are probably possible, but it only requires constant space and seems to work well.
#include <algorithm>
#include <cmath>
#include <random>
/** Zipf-like random distribution.
*
* "Rejection-inversion to generate variates from monotone discrete
* distributions", Wolfgang Hörmann and Gerhard Derflinger
* ACM TOMACS 6.3 (1996): 169-184
*/
template<class IntType = unsigned long, class RealType = double>
class zipf_distribution
{
public:
typedef RealType input_type;
typedef IntType result_type;
static_assert(std::numeric_limits<IntType>::is_integer, "");
static_assert(!std::numeric_limits<RealType>::is_integer, "");
zipf_distribution(const IntType n=std::numeric_limits<IntType>::max(),
const RealType q=1.0)
: n(n)
, q(q)
, H_x1(H(1.5) - 1.0)
, H_n(H(n + 0.5))
, dist(H_x1, H_n)
{}
IntType operator()(std::mt19937& rng)
{
while (true) {
const RealType u = dist(rng);
const RealType x = H_inv(u);
const IntType k = clamp<IntType>(std::round(x), 1, n);
if (u >= H(k + 0.5) - h(k)) {
return k;
}
}
}
private:
/** Clamp x to [min, max]. */
template<typename T>
static constexpr T clamp(const T x, const T min, const T max)
{
return std::max(min, std::min(max, x));
}
/** exp(x) - 1 / x */
static double
expxm1bx(const double x)
{
return (std::abs(x) > epsilon)
? std::expm1(x) / x
: (1.0 + x/2.0 * (1.0 + x/3.0 * (1.0 + x/4.0)));
}
/** H(x) = log(x) if q == 1, (x^(1-q) - 1)/(1 - q) otherwise.
* H(x) is an integral of h(x).
*
* Note the numerator is one less than in the paper order to work with all
* positive q.
*/
const RealType H(const RealType x)
{
const RealType log_x = std::log(x);
return expxm1bx((1.0 - q) * log_x) * log_x;
}
/** log(1 + x) / x */
static RealType
log1pxbx(const RealType x)
{
return (std::abs(x) > epsilon)
? std::log1p(x) / x
: 1.0 - x * ((1/2.0) - x * ((1/3.0) - x * (1/4.0)));
}
/** The inverse function of H(x) */
const RealType H_inv(const RealType x)
{
const RealType t = std::max(-1.0, x * (1.0 - q));
return std::exp(log1pxbx(t) * x);
}
/** That hat function h(x) = 1 / (x ^ q) */
const RealType h(const RealType x)
{
return std::exp(-q * std::log(x));
}
static constexpr RealType epsilon = 1e-8;
IntType n; ///< Number of elements
RealType q; ///< Exponent
RealType H_x1; ///< H(x_1)
RealType H_n; ///< H(n)
std::uniform_real_distribution<RealType> dist; ///< [H(x_1), H(n)]
};

The following line in your code is executed n times for each call to zipf():
sum_prob = sum_prob + c / pow((double) i, alpha);
It is regrettable that it is necessary to call the pow() function because, internally, this function sums not one but two Taylor series [considering that pow(x, alpha) == exp(alpha*log(x))]. If alpha is an integer, of course, then you can speed the code up a lot by replacing pow() with simple multiplication. If alpha is a rational number, then you may be able to speed the code up to a lesser degree by coding a Newton-Raphson iteration to take the place of the two Taylor series. If the last condition holds, please advise.
Fortunately, you have indicated that alpha does not change. Can you not speed the code up a lot by preparing a table of pow((double) i, alpha), then letting zipf() look numbers up the table? That way, zipf() would not have to call pow() at all. I suspect that this would save significant time.
Yet further improvements are possible. What if you factored a function sumprob() out of zipf()? Could you not prepare an even more aggressive look-up table for sumprob()'s use?
Maybe some of these ideas will move you in the right direction. See what you cannot do with them.
Update: I see that your question as now revised may not be able to use this answer. From the present point, your question may resolve into a question in complex variable theory. Such are often not easy questions, as you know. It may be that a sufficiently clever mathematician has discovered a relevant recurrence relation or some trick like the normal distribution's Box-Muller technique but, if so, I am not acquainted with the technique. Good luck. (It probably does not matter to you but, in case it does, the late N. N. Lebedev's excellent 1972 book Special Functions and Their Applications is available in English translation from the Russian in an inexpensive paperback edition. If you really, really wanted to crack this problem, you might read Lebedev next -- but, of course, that is a desperate measure, isn't it?)

As a complement to the very nice rejection-inversion implementation given above, here's a C++ class, with the same API, that is simpler and faster for a small number of bins, only. On my machine, its about 2.3x faster for N=300. It's faster because it performs a direct table lookup, instead of computing logs and powers. The table eats cache, though... Making a guess based on the size of my CPU's d-cache, I imagine that the proper rejection-inversion algo given above will become faster for something around N=35K, maybe. Also, initializing the table requires a call to std::pow() for each bin, so this wins performance only if you are drawing more than N values out of it. Otherwise, rejection-inversion is faster. Choose wisely.
(I've set up the API so it looks a lot like what the std::c++ standards committee might come up with.)
/**
* Example usage:
*
* std::random_device rd;
* std::mt19937 gen(rd());
* zipf_table_distribution<> zipf(300);
*
* for (int i = 0; i < 100; i++)
* printf("draw %d %d\n", i, zipf(gen));
*/
template<class IntType = unsigned long, class RealType = double>
class zipf_table_distribution
{
public:
typedef IntType result_type;
static_assert(std::numeric_limits<IntType>::is_integer, "");
static_assert(!std::numeric_limits<RealType>::is_integer, "");
/// zipf_table_distribution(N, s)
/// Zipf distribution for `N` items, in the range `[1,N]` inclusive.
/// The distribution follows the power-law 1/n^s with exponent `s`.
/// This uses a table-lookup, and thus provides values more
/// quickly than zipf_distribution. However, the table can take
/// up a considerable amount of RAM, and initializing this table
/// can consume significant time.
zipf_table_distribution(const IntType n,
const RealType q=1.0) :
_n(init(n,q)),
_q(q),
_dist(_pdf.begin(), _pdf.end())
{}
void reset() {}
IntType operator()(std::mt19937& rng)
{
return _dist(rng);
}
/// Returns the parameter the distribution was constructed with.
RealType s() const { return _q; }
/// Returns the minimum value potentially generated by the distribution.
result_type min() const { return 1; }
/// Returns the maximum value potentially generated by the distribution.
result_type max() const { return _n; }
private:
std::vector<RealType> _pdf; ///< Prob. distribution
IntType _n; ///< Number of elements
RealType _q; ///< Exponent
std::discrete_distribution<IntType> _dist; ///< Draw generator
/** Initialize the probability mass function */
IntType init(const IntType n, const RealType q)
{
_pdf.reserve(n+1);
_pdf.emplace_back(0.0);
for (IntType i=1; i<=n; i++)
_pdf.emplace_back(std::pow((double) i, -q));
return n;
}
};

Here's a version that is 2x faster than drobilla's original post, plus it also supports non-zero deformation parameter q (aka Hurwicz q, q-series q or quantum group deformation q) and changes notation to conform to standard usage in number theory textbooks. Rigorously tested; see unit tests at https://github.com/opencog/cogutil/blob/master/tests/util/zipfUTest.cxxtest
Dual license MIT license, or Gnu Affero, please copy into the C++ standard as desired.
/**
* Zipf (Zeta) random distribution.
*
* Implementation taken from drobilla's May 24, 2017 answer to
* https://stackoverflow.com/questions/9983239/how-to-generate-zipf-distributed-numbers-efficiently
*
* That code is referenced with this:
* "Rejection-inversion to generate variates from monotone discrete
* distributions", Wolfgang Hörmann and Gerhard Derflinger
* ACM TOMACS 6.3 (1996): 169-184
*
* Note that the Hörmann & Derflinger paper, and the stackoverflow
* code base incorrectly names the paramater as `q`, when they mean `s`.
* Thier `q` has nothing to do with the q-series. The names in the code
* below conform to conventions.
*
* Example usage:
*
* std::random_device rd;
* std::mt19937 gen(rd());
* zipf_distribution<> zipf(300);
*
* for (int i = 0; i < 100; i++)
* printf("draw %d %d\n", i, zipf(gen));
*/
template<class IntType = unsigned long, class RealType = double>
class zipf_distribution
{
public:
typedef IntType result_type;
static_assert(std::numeric_limits<IntType>::is_integer, "");
static_assert(!std::numeric_limits<RealType>::is_integer, "");
/// zipf_distribution(N, s, q)
/// Zipf distribution for `N` items, in the range `[1,N]` inclusive.
/// The distribution follows the power-law 1/(n+q)^s with exponent
/// `s` and Hurwicz q-deformation `q`.
zipf_distribution(const IntType n=std::numeric_limits<IntType>::max(),
const RealType s=1.0,
const RealType q=0.0)
: n(n)
, _s(s)
, _q(q)
, oms(1.0-s)
, spole(abs(oms) < epsilon)
, rvs(spole ? 0.0 : 1.0/oms)
, H_x1(H(1.5) - h(1.0))
, H_n(H(n + 0.5))
, cut(1.0 - H_inv(H(1.5) - h(1.0)))
, dist(H_x1, H_n)
{
if (-0.5 >= q)
throw std::runtime_error("Range error: Parameter q must be greater than -0.5!");
}
void reset() {}
IntType operator()(std::mt19937& rng)
{
while (true)
{
const RealType u = dist(rng);
const RealType x = H_inv(u);
const IntType k = std::round(x);
if (k - x <= cut) return k;
if (u >= H(k + 0.5) - h(k))
return k;
}
}
/// Returns the parameter the distribution was constructed with.
RealType s() const { return _s; }
/// Returns the Hurwicz q-deformation parameter.
RealType q() const { return _q; }
/// Returns the minimum value potentially generated by the distribution.
result_type min() const { return 1; }
/// Returns the maximum value potentially generated by the distribution.
result_type max() const { return n; }
private:
IntType n; ///< Number of elements
RealType _s; ///< Exponent
RealType _q; ///< Deformation
RealType oms; ///< 1-s
bool spole; ///< true if s near 1.0
RealType rvs; ///< 1/(1-s)
RealType H_x1; ///< H(x_1)
RealType H_n; ///< H(n)
RealType cut; ///< rejection cut
std::uniform_real_distribution<RealType> dist; ///< [H(x_1), H(n)]
// This provides 16 decimal places of precision,
// i.e. good to (epsilon)^4 / 24 per expanions log, exp below.
static constexpr RealType epsilon = 2e-5;
/** (exp(x) - 1) / x */
static double
expxm1bx(const double x)
{
if (std::abs(x) > epsilon)
return std::expm1(x) / x;
return (1.0 + x/2.0 * (1.0 + x/3.0 * (1.0 + x/4.0)));
}
/** log(1 + x) / x */
static RealType
log1pxbx(const RealType x)
{
if (std::abs(x) > epsilon)
return std::log1p(x) / x;
return 1.0 - x * ((1/2.0) - x * ((1/3.0) - x * (1/4.0)));
}
/**
* The hat function h(x) = 1/(x+q)^s
*/
const RealType h(const RealType x)
{
return std::pow(x + _q, -_s);
}
/**
* H(x) is an integral of h(x).
* H(x) = [(x+q)^(1-s) - (1+q)^(1-s)] / (1-s)
* and if s==1 then
* H(x) = log(x+q) - log(1+q)
*
* Note that the numerator is one less than in the paper
* order to work with all s. Unfortunately, the naive
* implementation of the above hits numerical underflow
* when q is larger than 10 or so, so we split into
* different regimes.
*
* When q != 0, we shift back to what the paper defined:
* H(x) = (x+q)^{1-s} / (1-s)
* and for q != 0 and also s==1, use
* H(x) = [exp{(1-s) log(x+q)} - 1] / (1-s)
*/
const RealType H(const RealType x)
{
if (not spole)
return std::pow(x + _q, oms) / oms;
const RealType log_xpq = std::log(x + _q);
return log_xpq * expxm1bx(oms * log_xpq);
}
/**
* The inverse function of H(x).
* H^{-1}(y) = [(1-s)y + (1+q)^{1-s}]^{1/(1-s)} - q
* Same convergence issues as above; two regimes.
*
* For s far away from 1.0 use the paper version
* H^{-1}(y) = -q + (y(1-s))^{1/(1-s)}
*/
const RealType H_inv(const RealType y)
{
if (not spole)
return std::pow(y * oms, rvs) - _q;
return std::exp(y * log1pxbx(oms * y)) - _q;
}
};

In the meantime there is a faster way based on rejection inversion sampling, see code here.

Related

Drake: Integrate Mass Matrix and Bias Term in Optimization Problem

I am trying to implement Non Linear MPC for a 7-DOF manipulator in drake. To do this, in my constraints, I need to have dynamic parameters like the Mass matrix M(q) and the bias term C(q,q_dot)*q_dot, but those depend on the decision variables q, q_dot.
I tried the following
// finalize plant
// create builder, diagram, context, plant context
...
// formulate optimazation problem
drake::solvers::MathematicalProgram prog;
// create decision variables
...
std::vector<drake::solvers::VectorXDecisionVariable> q_v;
std::vector<drake::solvers::VectorXDecisionVariable> q_ddot;
for (int i = 0; i < H; i++) {
q_v.push_back(prog.NewContinuousVariables<14>(state_var_name));
q_ddot.push_back(prog.NewContinuousVariables<7>(input_var_name));
}
// add cost
...
// add constraints
...
for (int i = 0; i < H; i++) {
plant.SetPositionsAndVelocities(*plant_context, q_v[i]);
plant.CalcMassMatrix(*plant_context, M);
plant.CalcBiasTerm(*plant_context, C_q_dot);
}
...
for (int i = 0; i < H; i++) {
prog.AddConstraint( M * q_ddot[i] + C_q_dot + G >= lb );
prog.AddConstraint( M * q_ddot[i] + C_q_dot + G <= ub );
}
// solve prog
...
The above code will not work, because plant.SetPositionsAndVelocities(.) doesn't accept symbolic variables.
Is there any way to integrate M,C in my ocp constraints ?
I think you want to impose the following nonlinear nonconvex constraint
lb <= M * qddot + C(q, v) + g(q) <= ub
This constraint is non-convex. We will need to solve it through nonlinear optimization, and evaluate the constraint in every iteration of the nonlinear optimization. We can't do this evaluation using symbolic computation (it would be horribly slow with symbolic computation).
So you will need a constraint evaluator, something like this
// This constraint takes [q;v;vdot] and evaluate
// M * vdot + C(q, v) + g(q)
class MyConstraint : public solvers::Constraint {
public:
MyConstraint(const MultibodyPlant<AutoDiffXd>& plant, systems::Context<AutoDiffXd>* context, const Eigen::Ref<const Eigen::VectorXd>& lb, const Eigen::Ref<const Eigen::VectorXd>& ub) : solvers::Constraint(plant.num_velocitiex(), plant.num_positions() + 2 * plant.num_velocities(), lb, ub), plant_{plant}, context_{context} {
...
}
private:
void DoEval(const Eigen::Ref<const AutoDiffVecXd>& x, AutoDiffVecXd* y) const {
...
}
MultibodyPlant<AutoDiffXd> plant_;
systems::Context<AutoDiffXd>* context_;
};
int main() {
...
// Construct the constraint and add it to every time instances
std::vector<std::unique_ptr<systems::Context<AutoDiffXd>>> plant_contexts;
for (int i = 0; i < H; ++i) {
plant_contexts.push_back(plant.CreateDefaultContext());
prog.AddConstraint(std::make_shared<MyConstraint>(plant, plant_context[i], lb, ub), {q_v[i], qddot[i]});
}
}
You could refer to the class CentroidalMomentumConstraint on how to construct your own MyConstraint class.

Eigen LLT (Cholesky) fails, while SVD works

I'm trying to reproduce some numpy code on Gaussian Processes (from here) using Eigen. Basically, I need to sample from a multivariate normal distribution:
samples = np.random.multivariate_normal(mu.ravel(), cov, 1)
The mean vector is currently zero, while the covariance matrix is a square matrix generated via the isotropic squared exponential kernel:
sqdist = np.sum(X1**2, 1).reshape(-1, 1) + np.sum(X2**2, 1) - 2 * np.dot(X1, X2.T)
return sigma_f**2 * np.exp(-0.5 / l**2 * sqdist)
I can generate the covariance matrix just fine for now (it can probably be cleaned but for now it's a POC):
Matrix2D kernel(const Matrix2D & x1, const Matrix2D & x2, double l = 1.0, double sigma = 1.0) {
auto dists = ((- 2.0 * (x1 * x2.transpose())).colwise()
+ x1.rowwise().squaredNorm()).rowwise() +
+ x2.rowwise().squaredNorm().transpose();
return std::pow(sigma, 2) * ((-0.5 / std::pow(l, 2)) * dists).array().exp();
}
However, my problems start when I need to sample the multivariate normal.
I've tried using the solution proposed in this accepted answer; however, the decomposition only works with covariance matrices of size up to 30x30; more than that and LLT fails to decompose the matrix. The alternative version provided in the answer also does not work, and creates NaNs. I tried LDLT as well but it also breaks (D contains negative values, so sqrt gives NaN).
Then, I got curious, and I looked into how numpy does this. Turns out the numpy implementation uses SVD decomposition (with LAPACK), rather than Cholesky. So I tried copying their implementation:
// SVD on the covariance matrix generated via kernel function
Eigen::BDCSVD<Matrix2D> solver(covs, Eigen::ComputeFullV);
normTransform = (-solver.matrixV().transpose()).array().colwise() * solver.singularValues().array().sqrt();
// Generate gaussian samples, "randN" is from the multivariate StackOverflow answer
Matrix2D gaussianSamples = Eigen::MatrixXd::NullaryExpr(1, means.size(), randN);
Eigen::MatrixXd samples = (gaussianSamples * normTransform).rowwise() + means.transpose();
The various minuses are me trying to exactly reproduce numpy's results.
In any case, this works perfectly fine, even with large dimensions. I was wondering why Eigen is not able to do LLT, but SVD works. The covariance matrix I use is the same. Is there something I can do to simply use LLT?
EDIT: Here is my full example:
#include <iostream>
#include <random>
#include <Eigen/Cholesky>
#include <Eigen/SVD>
#include <Eigen/Eigenvalues>
using Matrix2D = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::AutoAlign>;
using Vector = Eigen::Matrix<double, Eigen::Dynamic, 1>;
/*
We need a functor that can pretend it's const,
but to be a good random number generator
it needs mutable state.
*/
namespace Eigen {
namespace internal {
template<typename Scalar>
struct scalar_normal_dist_op
{
static std::mt19937 rng; // The uniform pseudo-random algorithm
mutable std::normal_distribution<Scalar> norm; // The gaussian combinator
EIGEN_EMPTY_STRUCT_CTOR(scalar_normal_dist_op)
template<typename Index>
inline const Scalar operator() (Index, Index = 0) const { return norm(rng); }
};
template<typename Scalar> std::mt19937 scalar_normal_dist_op<Scalar>::rng;
template<typename Scalar>
struct functor_traits<scalar_normal_dist_op<Scalar> >
{ enum { Cost = 50 * NumTraits<Scalar>::MulCost, PacketAccess = false, IsRepeatable = false }; };
} // end namespace internal
} // end namespace Eigen
Matrix2D kernel(const Matrix2D & x1, const Matrix2D & x2, double l = 1.0, double sigma = 1.0) {
auto dists = ((- 2.0 * (x1 * x2.transpose())).colwise() + x1.rowwise().squaredNorm()).rowwise() + x2.rowwise().squaredNorm().transpose();
return std::pow(sigma, 2) * ((-0.5 / std::pow(l, 2)) * dists).array().exp();
}
int main() {
unsigned size = 50;
unsigned seed = 1;
Matrix2D X = Vector::LinSpaced(size, -5.0, 4.8);
Eigen::internal::scalar_normal_dist_op<double> randN; // Gaussian functor
Eigen::internal::scalar_normal_dist_op<double>::rng.seed(seed); // Seed the rng
Vector means = Vector::Zero(X.rows());
auto covs = kernel(X, X);
Eigen::LLT<Matrix2D> cholSolver(covs);
// We can only use the cholesky decomposition if
// the covariance matrix is symmetric, pos-definite.
// But a covariance matrix might be pos-semi-definite.
// In that case, we'll go to an EigenSolver
Eigen::MatrixXd normTransform;
if (cholSolver.info()==Eigen::Success) {
std::cout << "Used LLT\n";
// Use cholesky solver
normTransform = cholSolver.matrixL();
} else {
std::cout << "Broken\n";
Eigen::BDCSVD<Matrix2D> solver(covs, Eigen::ComputeFullV);
normTransform = (-solver.matrixV().transpose()).array().colwise() * solver.singularValues().array().sqrt();
}
Matrix2D gaussianSamples = Eigen::MatrixXd::NullaryExpr(1, means.size(), randN);
Eigen::MatrixXd samples = (gaussianSamples * normTransform).rowwise() + means.transpose();
return 0;
}

Doubles rounding again

In my program there are some precisions (some positive integer, in the most cases it supposed to be of the form ) for some doubles, so that double * precision should become an integer.
But as we all know floating point numbers are inaccurate, so, for example 1.3029515 could be saved as 1.3029514999999998..., and in my program I need to write such floating point number to a file, but I want this 1.3029515 to be written instead of something like 1.3029514999999998....
Previously only precision of form was used in my program, and I've reached the wanted result with a piece of code like below:
// I have a function for doubles equality check
inline bool sameDoubles(const double& lhs, const double& rhs, const double& epsilon) {
return fabs(lhs - rhs) < epsilon;
}
inline void roundDownDouble(double& value, const unsigned int& numberOfDigitsInFraction = 6) {
assert(numberOfDigitsInFraction <= 9);
double factor = pow(10.0, numberOfDigitsInFraction);
double oldValue = value;
value = (((int)(value * factor)) / factor);
// when, for example, 1.45 is stored as 1.4499999999..., we can get wrong value, so, need to do the check below
double diff = pow(10.0, 0.0 - numberOfDigitsInFraction);
if(sameDoubles(diff, fabs(oldValue - value), 1e-9)) {
value += diff;
}
};
But now, I can't reach wanted results with the same technique, I've tried with a function below, but have not succeeded:
// calculates logarithm of number with given base
double inline logNbase(double number, double base) {
return log(number)/log(base);
}
// sameDoubles function is the same as in above case
inline void roundDownDouble(double& value, unsigned int precision = 1e+6) {
if(sameDoubles(value, 0.0)) { value = 0; return; }
double oldValue = value;
value = ((long int)(value * precision) / (double)precision);
// when, for example, 1.45 is stored as 1.4499999999..., we can get wrong value, so, need to do the check below
int pwr = (int)(logNbase((double)precision, 10.0));
long int coeff = precision / pow(10, pwr);
double diff = coeff * pow(10, -pwr);
if(sameDoubles(diff, fabs(oldValue - value), diff / 10.0)) {
if(value > 0.0) {
value += diff;
} else {
value -= diff;
}
}
}
For 1.3029515 value and precision = 2000000 this function returns incorrect 1.302951 value (expression (long int)(value * precision) becomes equal to 2605902 instead of 2605903).
How can I fix this? Or maybe there is a some smart way to make this rounding happen correctly?
You're doing your rounding the hard way. Do it the easy way instead:
double rounding = 0.5;
if (value < 0.0) rounding = -0.5;
value = ((long int)(value * precision + rounding) / (double)precision);
Now there's no need for the rest of the code.

Limit values of struct member [duplicate]

This question already has answers here:
Fastest way to clamp a real (fixed/floating point) value?
(14 answers)
Closed 7 years ago.
I want to create a simple struct that stores the RGB-values of a color. r, g and b are supposed to be double numbers in [0,1].
struct Color
{
Color(double x): r{x}, g{x}, b{x} {
if (r < 0.0) r = 0.0;
if (r > 1.0) r = 1.0;
if (g < 0.0) g = 0.0;
if (g > 1.0) g = 1.0;
if (b < 0.0) b = 0.0;
if (b > 1.0) b = 1.0;
}
}
Is there a better way than using those if statements?
Just write a function to clamp:
double clamp(double val, double left = 0.0, double right = 1.0) {
return std::min(std::max(val, left), right);
}
And use that in your constructor:
Color(double x)
: r{clamp(x)}
, g{clamp(x)}
, b{clamp(x)}
{ }
You, can can use min and max, ideally combining them into a clamp function:
template <class T>
T clamp(T val, T min, T max)
{
return std::min(max, std::max(min, val));
}
struct Color
{
Color(double x) : r{clamp(x, 0., 1.)}, g{clamp(x, 0., 1.)}, b{clamp(x, 0., 1.)}
{}
};
For a first pass iteration, we have min/max functions we can and should use:
struct Color
{
explicit Color(double x): r{x}, g{x}, b{x}
{
r = std::max(r, 0.0);
r = std::min(r, 1.0);
g = std::max(g, 0.0);
g = std::min(g, 1.0);
b = std::max(b, 0.0);
b = std::min(b, 1.0);
}
double r, g, b;
};
I'd also suggest making that constructor explicit, as it's rather confusing for a scalar to implicitly convert to a Color.
The reason this is arguably an upgrade even with roughly the same amount of code and arguably not the biggest improvement in readability is because, while optimizing compilers might emit faster branchless code here, min and max can often guarantee an efficient implementation. You're also expressing what you're doing in a slightly more direct way.
There is some truth to this somewhat counter-intuitive idea that writing higher level code helps you achieve efficiency, if only for the reason that the low-level logic used to implement the high-level function is more likely to be efficient than what people would repeatedly write otherwise in their more casual, daily kind of code. It also helps direct your codebase towards more central targets for optimization.
As a second pass, this may not improve things for your particular use cases, but in general I've found it's useful to represent color and vector components using an array to allow you to access them with loops. This is because if you start doing somewhat complex things with colors like blending them together, the logic for each color component is non-trivial but identical for all components, so you don't want to end up writing such code three times all the time or always be forced into writing the per-component logic in a separate function or anything like that.
So we might do this:
class Color
{
public:
explicit Color(double x)
{
for (int j=0; j < 3; ++j)
{
rgb[j] = x;
rgb[j] = std::max(rgb[j], 0.0);
rgb[j] = std::min(rgb[j], 1.0);
}
}
// Bounds-checking assertions in these would also be a nice idea.
double& operator[](int n) {return rgb[n]};
double operator[](int n) const {return rgb[n]};
double& red() {return rgb[0];}
double red() const {return rgb[0];}
double& green() {return rgb[1];}
double green() const {return rgb[1];}
double& blue() {return rgb[2];}
double blue() const {return rgb[2];}
// Somewhat excess fluff, but such methods can be useful when
// interacting with a low-level C-style API (OpenGL, e.g.) as
// opposed to using &color.red() or &color[0].
double* data() {return rgb;}
const double* data() const {return rgb;}
private:
double rgb[3];
};
Finally, as others have mentioned, this is where a function to clamp values to a range is useful, so as a final pass:
template <class T>
T clamp(T val, T low, T high)
{
assert(low <= high);
return std::max(std::min(val, high), low);
}
// New constructor using clamp:
explicit Color(double x)
{
for (int j=0; j < 3; ++j)
rgb[j] = clamp(x, 0.0, 1.0);
}

Implementing the ratio of uniforms for normal distribution in C++

I understand that this can be done by defining a teardrop shape and accepting points that fall within that region (from a uniform generator).
I am trying to do this in C++ by generating two uniform random numbers x and y to locate the point (x,y), and then checking if this point falls within the region.
I don't have a problem with the code itself, but is there a flaw in my logic here? I haven't found a suitable graphical way to check if this is true normal distribution yet.
Here is the code which is supposed to work:
typedef unsigned long long int Ullong;
typedef double Doub;
struct Normaldev : Ran {
Doub mu,sig;
Normaldev (Doub mmu, Doub ssig, Ullong i)
: Ran (i), mu(mmu), sig(ssig){}
Doub dev() {
Doub u, v, x, y, q;
do {
u=Doub();
v=1.7156*(Doub()-0.5);
x=u-0.449871;
y=abs(v)+0.386595;
q=x*x+y*(0.19600*y-0.25472*x);
} while(q>0.27597 && (q>0.27846 || v*v>-4*log(u)*u*u));
return mu+sig*v/u;
}
};
I changed the suggested code in the Numerical Recipes book as much as I could with my rudimentary knowledge of C++, but what exactly is Ran supposed to be?
I changed the suggested code in the Numerical Recipes book as much as I could with my rudimentary knowledge of C++, but what exactly is Ran supposed to be?
Ran is a parent class of NormalDev. Its not defined in the code you gave. Based on the code it seems to be a pretty generic Random number class that takes an unsigned long long int seed in its constructor.
Have a look at the 3rd Edition of "Numerical Recipes in C", pages 364-369. You'll find out that Box-Muller returns two normally distributed random variables, namely 'u' and 'v'. So the first time the function is called, you calculate both variables but return just 'u'. The second call of the function does nothing but returning 'v'.
double u, v;
double sigma = 1.0
double mean = 0.0;
int flag = 0;
double boxMuller()
{
if (flag == 1) {
flag = 0;
return v * sigma + mean;
}
double help;
do {
u = Doub() - 0.5;
v = Doub() - 0.5;
help = u * u + v * v;
} while (help >= 0.25);
help = sqrt( log( help * 4.0 ) / help * -2.0 );
u *= help;
v *= help;
flag = 1;
return u * sigma + mean;
}
The Ratio-of-Uniforms method in contrast has to calculate a new random variable on every call (not every second).
So i measured the times, and prefer using the Box-Muller code above.