C++ 如何创建自定义随机分布函数?
通常我使用生成值,但现在我需要创建表单的随机分布C++ 如何创建自定义随机分布函数?,c++,c++11,math,random,C++,C++11,Math,Random,通常我使用生成值,但现在我需要创建表单的随机分布 f(x) = k*log(x) + m 可以定义自定义随机分布函数吗?对于我的实际模型,我有x=[1,1.4e7),k=-0.905787102751,m=14.913170454。理想情况下,我希望它能像当前内置分布那样工作: int main() { std::mt19937 generator; std::uniform_real_distribution<> dist(0.0, 1.0); my_
f(x) = k*log(x) + m
可以定义自定义随机分布函数吗?对于我的实际模型,我有x=[1,1.4e7),k=-0.905787102751,m=14.913170454
。理想情况下,我希望它能像当前内置分布那样工作:
int main()
{
std::mt19937 generator;
std::uniform_real_distribution<> dist(0.0, 1.0);
my_distribution my_dist(0.0, 10.0); // Distribution using f(x)
double uni_val = dist(generator);
double log_val = my_dist(generator);
}
intmain()
{
标准:mt19937发电机;
标准:均匀实分布区(0.0,1.0);
my_distribution my_dist(0.0,10.0);//使用f(x)的分布
双单位值=距离(发电机);
双对数=我的距离(发电机);
}
这是非常可能的,但它是一个C++问题的数学问题。创建伪随机数发生器的最一般方法是:基本上,任何PDF的CDF均匀分布在0和1之间(如果这不明显,只要记住CDF的值是一个概率,并在此考虑)。因此,您只需对0和1之间的随机均匀数进行采样,然后应用CDF的倒数
在你的例子中,用F(x)=k*log(x)+m $(你没有指定界限,但我假设它们在1和一些正数>1)CDF及其逆是相当混乱的问题-我留给你!C++中的实现看起来像
double inverseCDF(double p, double k, double m, double lowerBound, double upperBound) {
// do math, which might include numerically finds roots of equations
}
然后生成代码将类似于
class my_distribution {
// ... constructor, private variables, etc.
template< class Generator >
double operator()( Generator& g ) {
std::uniform_real_distribution<> dist(0.0, 1.0);
double cdf = dist(g);
return inverseCDF(cdf,this->k,this->m,this->lowerBound,this->upperBound);
}
}
class my\u分布{
//…构造函数、私有变量等。
模板<类生成器>
双运算符()(发电机和发电机){
标准:均匀实分布区(0.0,1.0);
双cdf=距离(g);
返回反向ECDF(cdf,this->k,this->m,this->lowerBound,this->upperBound);
}
}
我完全遵循了@jwimberley的想法,并认为我会在这里分享我的结果。我创建了一个类,该类可以执行以下操作:
构造函数参数:
- (正常化或非正常化),这是
函数的积分
- 分布的上下限
- (可选)分辨率,指示我们应获取多少CDF采样点
从CDF->随机数x计算映射。这是我们的反向CDF函数
通过以下方式生成随机点:
- 使用
std::random
生成(0,1]
之间的随机概率p
- 在映射中对对应于p的CDF值进行二进制搜索。返回与CDF一起计算的x。提供附近“bucket”之间的可选线性积分,否则我们将得到n==分辨率离散步数
守则:
// sampled_distribution.hh
#ifndef SAMPLED_DISTRIBUTION
#define SAMPLED_DISTRIBUTION
#include <algorithm>
#include <vector>
#include <random>
#include <stdexcept>
template <typename T = double, bool Interpolate = true>
class Sampled_distribution
{
public:
using CDFFunc = T (*)(T);
Sampled_distribution(CDFFunc cdfFunc, T low, T high, unsigned resolution = 200)
: mLow(low), mHigh(high), mRes(resolution), mDist(0.0, 1.0)
{
if (mLow >= mHigh) throw InvalidBounds();
mSampledCDF.resize(mRes + 1);
const T cdfLow = cdfFunc(low);
const T cdfHigh = cdfFunc(high);
T last_p = 0;
for (unsigned i = 0; i < mSampledCDF.size(); ++i) {
const T x = i/mRes*(mHigh - mLow) + mLow;
const T p = (cdfFunc(x) - cdfLow)/(cdfHigh - cdfLow); // normalising
if (! (p >= last_p)) throw CDFNotMonotonic();
mSampledCDF[i] = Sample{p, x};
last_p = p;
}
}
template <typename Generator>
T operator()(Generator& g)
{
T cdf = mDist(g);
auto s = std::upper_bound(mSampledCDF.begin(), mSampledCDF.end(), cdf);
auto bs = s - 1;
if (Interpolate && bs >= mSampledCDF.begin()) {
const T r = (cdf - bs->prob)/(s->prob - bs->prob);
return r*bs->value + (1 - r)*s->value;
}
return s->value;
}
private:
struct InvalidBounds : public std::runtime_error { InvalidBounds() : std::runtime_error("") {} };
struct CDFNotMonotonic : public std::runtime_error { CDFNotMonotonic() : std::runtime_error("") {} };
const T mLow, mHigh;
const double mRes;
struct Sample {
T prob, value;
friend bool operator<(T p, const Sample& s) { return p < s.prob; }
};
std::vector<Sample> mSampledCDF;
std::uniform_real_distribution<> mDist;
};
#endif
使用以下工具运行演示:
clang++ -std=c++11 -stdlib=libc++ main.cc -o main; ./main; python dist_plot.py
正如其他地方指出的,对任何PDF进行采样的标准方法是在从区间[0,1]中均匀随机选择的点处反转其CDF
对于您的特定问题,CDF是一个简单的函数,但它的逆函数不是。在这种情况下,可以使用传统的数值工具(如牛顿-拉斐逊迭代)将其反转。不幸的是,您没有指定x
的范围,也没有指定m
和k
参数的允许选择为此,我们对代码进行了修改,代码> > k>代码>和范围()以满足C++, ,我喜欢这里提出的许多概念,导致一个非常纤细但非常强大的生成器。我刚刚做了一些清理嵌入C++ 17个特性,我要编辑Punl的答案,但是它却完全不同,所以我把它分开了。
#pragma once
#include <algorithm>
#include <vector>
#include <random>
#include <stdexcept>
template <typename T = double, bool Interpolate = true>
class SampledDistribution {
struct Sample {
T prob, value;
Sample(const T p, const T v): prob(p), value(v) {}
friend bool operator<(T p, const Sample& s) { return p < s.prob; }
};
std::vector<Sample> SampledCDF;
public:
struct InvalidBounds: std::runtime_error { using std::runtime_error::runtime_error; };
struct CDFNotMonotonic: std::runtime_error { using std::runtime_error::runtime_error; };
template <typename F>
SampledDistribution(F&& cdfFunc, const T low, const T high, const unsigned resolution = 256) {
if (low >= high) throw InvalidBounds("");
SampledCDF.reserve( resolution );
const T cdfLow = cdfFunc(low);
const T cdfHigh = cdfFunc(high);
for (unsigned i = 0; i < resolution; ++i) {
const T x = (high - low)*i/(resolution-1) + low;
const T p = (cdfFunc(x) - cdfLow)/(cdfHigh - cdfLow); // normalising
if (p < SampledCDF.back()) throw CDFNotMonotonic("");
SampledCDF.emplace_back(p, x);
}
}
template <typename Engine>
T operator()(Engine& g) {
const T cdf = std::uniform_real_distribution<T>{0.,1.}(g);
auto s = std::upper_bound(SampledCDF.begin(), SampledCDF.end(), cdf);
if (Interpolate && s != SampledCDF.begin()) {
auto bs = s - 1;
const T r = (cdf - bs->prob)/(s->prob - bs->prob);
return r*bs->value + (1 - r)*s->value;
}
return s->value;
}
};
例如,C++的数学是这个问题。例如,看看什么是域?@ YVISDAUST,初始问题是在1 -1.4E7之间。我为我如何解决它添加了一个答案。请指定参数的期望范围:代码>代码> M>代码>和代码> K<代码>以及范围。>@Walter我在问题中添加了我的实际模型值作为编辑。谢谢。这是一个很好的建议,引导我走上了正确的道路。我添加了一个答案,概述了我是如何实现它的——这是你的想法吗?如果你觉得有什么问题,请提出改进建议。关于这段代码,有几点可以说,bu“这真的属于代码审查。@Walter这篇文章不要求审查。这是我如何创建自定义随机分布的答案,回答了我自己的问题。我真的对否决票感到惊讶。你的代码远远不是最优的。首先,你至少应该测试CDF的单调性。其次,你可以实现一种更好的方法来例如,使用样条曲线或多项式插值将其反转。第三,如果用户要求同时使用PDF和CDF,则可以使用牛顿-拉斐逊(Newton-Raphson)将后者反转,这可以收敛到机器精度。最后,这对于您的初始问题来说是过度的。@Walter我感谢您的反馈。您的所有评论都有价值——我的经验ce在这个问题上是有限的,我尽了最大的努力来实现它,以满足我的需要。关于它有点过分:我试图遵循jwimberley的想法,我能做些什么呢?@Walter应该没有必要测试单调性,一个有效的CDF总是单调的非递减的,因为任何有效的PDF都必须是非负的但必须修改离散分布的二进制搜索。
clang++ -std=c++11 -stdlib=libc++ main.cc -o main; ./main; python dist_plot.py
#pragma once
#include <algorithm>
#include <vector>
#include <random>
#include <stdexcept>
template <typename T = double, bool Interpolate = true>
class SampledDistribution {
struct Sample {
T prob, value;
Sample(const T p, const T v): prob(p), value(v) {}
friend bool operator<(T p, const Sample& s) { return p < s.prob; }
};
std::vector<Sample> SampledCDF;
public:
struct InvalidBounds: std::runtime_error { using std::runtime_error::runtime_error; };
struct CDFNotMonotonic: std::runtime_error { using std::runtime_error::runtime_error; };
template <typename F>
SampledDistribution(F&& cdfFunc, const T low, const T high, const unsigned resolution = 256) {
if (low >= high) throw InvalidBounds("");
SampledCDF.reserve( resolution );
const T cdfLow = cdfFunc(low);
const T cdfHigh = cdfFunc(high);
for (unsigned i = 0; i < resolution; ++i) {
const T x = (high - low)*i/(resolution-1) + low;
const T p = (cdfFunc(x) - cdfLow)/(cdfHigh - cdfLow); // normalising
if (p < SampledCDF.back()) throw CDFNotMonotonic("");
SampledCDF.emplace_back(p, x);
}
}
template <typename Engine>
T operator()(Engine& g) {
const T cdf = std::uniform_real_distribution<T>{0.,1.}(g);
auto s = std::upper_bound(SampledCDF.begin(), SampledCDF.end(), cdf);
if (Interpolate && s != SampledCDF.begin()) {
auto bs = s - 1;
const T r = (cdf - bs->prob)/(s->prob - bs->prob);
return r*bs->value + (1 - r)*s->value;
}
return s->value;
}
};
#include <iostream>
#include "SampledDistribution.hpp"
int main() {
std::mt19937 gen;
auto sinFunc = [](double x) { return x + std::cos(x); }; // PDF(x) = 1 - sin(x)
unsigned resolution = 32;
std::vector<int> v(resolution,0);
SampledDistribution dist(sinFunc, 0.0, 6.28, resolution);
for (int i = 0; i < 100000; i++)
++v[ static_cast<size_t>(dist(gen)/(6.28) * resolution) ];
for (auto i: v)
std::cout << i << '\t' << std::string(i/100, '*') << std::endl;
return 0;
}
$ g++ -std=c++17 main.cpp && ./a.out
2882 ****************************
2217 **********************
1725 *****************
1134 ***********
690 ******
410 ****
182 *
37
34
162 *
411 ****
753 *******
1163 ***********
1649 ****************
2157 *********************
2796 ***************************
3426 **********************************
4048 ****************************************
4643 **********************************************
5193 ***************************************************
5390 *****************************************************
5796 *********************************************************
5979 ***********************************************************
6268 **************************************************************
6251 **************************************************************
6086 ************************************************************
5783 *********************************************************
5580 *******************************************************
5111 ***************************************************
4646 **********************************************
3964 ***************************************
3434 **********************************