如何生成与直方图匹配的点?

我正在研究一个模拟系统。 我将很快获得实验数据(直方图),用于几个模拟输入的实际值分布。

当模拟运行时,我希望能够生成与测量分布匹配的随机值。 我宁愿这样做而不存储原始直方图。 什么是好方法

  1. 将直方图映射到表示分布的一组参数?
  2. 在运行时生成基于这些参数的值?

编辑:输入数据是几种不同类型事件的事件持续时间。 我希望不同的类型具有不同的分布函数。

至少有两种选择:

  1. 整合直方图并以数字方式反转。
  2. 拒绝

数字集成

来自William R. Gibbs的现代物理计算

人们可以总是数字地整合[函数]并反转[ cdf ],但这通常不是很令人满意,特别是如果pdf正在快速变化。

您实际上构建了一个表,将范围[0-1)转换为目标分布中的适当范围。 扔掉你平常的(高质量的)PRNG并用桌子翻译。 它很麻烦,但清晰,可行,而且完全一般。

拒绝:

然后标准化目标直方图

  1. 扔骰子以随机选择沿该范围的位置( x )。
  2. 再次抛出,如果新的随机数小于此箱中的标准化直方图,则选择此点。 否则转到(1)。

再次,简单明了但清晰而有效。 它的分布速度很慢,概率非常低(长尾峰)。


使用这两种方法,如果不需要阶梯函数直方图,您可以使用分段多项式拟合或样条来近似数据以生成平滑曲线 – 但请稍后将其保留,因为它可能是过早优化。


特殊情况可能存在更好的方法。

所有这些都是非常标准的,如果我需要更多详细信息,它应该出现在任何数字分析教科书中。

有关该问题的更多信息将非常有用。 例如,直方图是什么类型的值? 它们是绝对的(例如,颜色,字母)还是连续的(例如,高度,时间)?

如果直方图超过分类数据,我认为除非类别之间存在许多相关性,否则可能难以对分布进行参数化。

如果直方图超过连续数据,您可能会尝试使用高斯混合物拟合分布。 也就是说,尝试使用$ \ sum_ {i = 1} ^ n w_i N(m_i,v_i)$拟合直方图,其中m_i和v_i是均值和方差。 然后,当你想要生成数据时,你首先从1..n中采样i,其概率与权重w_i成比例,然后像任何高斯一样采样x~n(m_i,v_i)。

无论哪种方式,您可能想要阅读有关混合模型的更多信息。

因此,似乎我想要生成给定概率分布的是分位数函数 ,它是累积分布函数的反函数 ,正如@dmckee所说的那样。

问题变成:生成和存储描述给定连续直方图的分位数函数的最佳方法是什么? 我有一种感觉,答案将在很大程度上取决于输入的形状 – 如果它遵循任何类型的模式,那么应该在最一般的情况下进行简化。 我会在这里更新。


编辑:

本周我进行了一次谈话,让我想起了这个问题。 如果我放弃将直方图描述为方程式,并且只存储表格,我可以在O(1)时间内进行选择吗? 事实certificate,您可以在不损失精度的情况下,以O(N lgN)施工时间为代价。

创建N个项目的数组。 对arrays的均匀随机选择将找到具有概率1 / N的项目。 对于每个项目,存储实际应该选择此项目的命中部分,以及如果不存在该项目将选择的另一项目的索引。

加权随机抽样,C实现:

 //data structure typedef struct wrs_data { double share; int pair; int idx; } wrs_t; //sort helper int wrs_sharecmp(const void* a, const void* b) { double delta = ((wrs_t*)a)->share - ((wrs_t*)b)->share; return (delta<0) ? -1 : (delta>0); } //Initialize the data structure wrs_t* wrs_create(int* weights, size_t N) { wrs_t* data = malloc(sizeof(wrs_t)); double sum = 0; int i; for (i=0;i0 && i= 0) { check=j--;} } } return data; } int wrs_pick(wrs_t* collection, size_t N) //O(1) weighted random sampling (after preparing the collection). //Randomly select a bucket, and a percentage. //If the percentage is greater than that bucket's share of hits, // use it's paired bucket. { int idx = rand_in_range(0,N); double pct = rand_percent(); if (pct > collection[idx].share) { idx = collection[idx].pair; } return collection[idx].idx; } 

编辑2:经过一番研究后,我发现甚至可以在O(N)时间进行构造。 通过仔细跟踪,您无需对数组进行排序即可找到大型和小型垃圾箱。 这里更新了实施

如果您需要使用加权分布的离散点来提取大量样本,请查看类似问题的答案 。

但是,如果您需要使用直方图来近似某些连续随机函数,那么您最好的选择可能是dmckee的数值积分答案。 或者,您可以使用锯齿,并将点存储在左侧,并在两点之间选择一个统一的数字。

要从直方图(原始或简化)中进行选择, Walker的别名方法快速而简单。