【问题标题】:Fast adding random variables in C++在 C++ 中快速添加随机变量
【发布时间】:2012-10-14 06:09:59
【问题描述】:

短版:如何最有效地表示和添加由它们的实现列表给出的两个随机变量?

略长的版本: 对于工作项目,我需要添加几个随机变量,每个变量都由值列表给出。例如,rand 的实现。变种。 A 是 {1,2,3},B 的实现是 {5,6,7}。因此,我需要的是A+B的分布,即{1+5,1+6,1+7,2+5,2+6,2+7,3+5,3+6,3+7 }。而且我需要对不同的随机变量(C,D,...)进行多次这种添加(让我们将此添加次数表示为 COUNT,其中 COUNT 可能达到 720)。

问题:如果我使用这种将 A 的每个实现与 B 的每个实现相加的愚蠢算法,复杂度在 COUNT 中是指数级的。因此,对于每个 r.v.由三个值给出,COUNT=720 的计算量是 3^720 ~ 3.36xe^343,这将持续到我们的日子结束来计算:) 更不用说在现实生活中,每个 r.v. 的长度。会是5000+。

解决方案: 1/第一个解决方案是使用我可以进行舍入的事实,即具有整数值的实现。像这样,我可以代表每个 r.v.作为一个向量,并且在对应于实现的索引处,我的值为 1(当 r.v. 具有此实现一次时)。所以对于房车A 和一个从 0 到 10 索引的实现向量,表示 A 的向量将是 [0,1,1,1,0,0,0...],而 B 的表示将是 [0,0,0, 0,0,1,1,1,0,0,10]。现在我通过这些向量创建 A+B 并执行与上述相同的操作(将 A 的每个实现与 B 的每个实现相加,并将其编码为相同的向量结构,向量长度的二次复杂度)。这种方法的好处是复杂性是有限的。这种方法的问题是,在实际应用中,A的实现会在区间[-50000,50000]中,粒度为1。因此,添加两个随机变量后,A+B的跨度达到-100K , 100K .. 在 720 次加法之后,SUM(A, B, ...) 的跨度达到 [-36M, 36M] 甚至在这么大的数组上的二次复杂度(与指数复杂度相比)将永远持续下去。

2/ 要拥有更短的数组,可以使用哈希图,这很可能会减少 A+B 中涉及的操作(数组访问)的数量,因为假设理论跨度的一些重要部分 [ -50K, 50K] 永远不会成为现实。然而,随着越来越多的随机变量的不断求和,实现的数量呈指数增长,而跨度仅线性增加,因此跨度中的数字密度随着时间的推移而增加。这会扼杀 hashmap 的好处。

所以问题是:我怎样才能有效地解决这个问题?电力交易中的VaR计算需要解决方案,所有分布都是凭经验给出的,不像普通分布,因此公式没有用,我们只能模拟。


使用数学被认为是我们部门的第一选择。是数学家。但是,我们要添加的分布表现不佳,并且 COUNT=720 是一个极端值。更有可能的是,我们将使用 COUNT=24 作为每日 VaR。考虑到要添加的分布的不良行为,对于 COUNT=24,中心极限定理不会太紧密(SUM(A1, A2, ..., A24) 的发行版不会接近正常值)。在计算可能的风险时,我们希望得到一个尽可能准确的数字。

预期用途是:您从某些操作中获得每小时的现金流量。一小时的现金流分布是 r.v. A. 接下来的一个小时是 r.v. B 等等。而你的问题是:在 99% 的案例中,最大的损失是什么?因此,您对这 24 小时中的每一个小时的现金流量进行建模,并将这些现金流量作为随机变量添加,以便获得全天总现金流量的分布。然后你取 0.01 分位数。

【问题讨论】:

  • 我觉得你的复杂度计算有误,应该是720^3而不是3^720。
  • @Skizz。当我有 A={1,2,3}, B={4,5,6} 时,生成的 A+B 发行版需要 3*3 整数加法。结果是 {4,5,6,8,10,12,12,15,18}。添加另一个房车C={7,8,9},我有 9*3 个整数加法。因此,每个添加的 r.v.整数加法的次数增加了三倍。
  • @DanBencik 你刚刚成倍增加......我以为你应该添加。
  • 我现在明白了,问题开始变得有意义了。
  • @paddy:你是对的。所以 A+B 应该是 {5,6,7,6,7,8,7,8,9}。我很抱歉。

标签: c++ performance random


【解决方案1】:

尝试减少完成整个添加所需的传递次数,可能将每个列表(包括最后一个列表)的传递次数减少到一次。

我认为你不能减少添加的总数。

此外,如果适用,您应该研究并行算法和多线程。

此时,只要给出适当的指令 (SSE),大多数处理器都能够并行执行加法运算,这将使加法运算速度提高很多倍(仍然不能解决复杂性问题)。

【讨论】:

  • 当然,我会在树状添加方案中添加 log(720)/log(2) 次,而不是添加 720 次。但正如你所说,它并没有解决复杂性问题。至于 SSE,我很想使用它,但到目前为止,我看不到将它应用到哪里,因为对于 A=[0,1,0,0,0] 没有索引的从 0 到 5 的表示和 B=[0,0,1,0,0] A+B 表示为 [0,1,1,0,0]。相反,A+B 是 [0,0,0,1,0,0]。这就是为什么我的部分问题是如何最有效地表示这些随机变量(可能允许使用 SSE)。但是还是非常感谢!
  • 可以根据问题中的描述减少添加的总数。看我的回答!
  • @Dan:我肯定会这么做的!
【解决方案2】:

正如您在问题中所说,您将需要大量的计算才能获得确切的答案。所以这不会发生。

但是,当您处理随机值时,可以将一些数学知识应用于问题。所有这些添加的结果不会导致接近正态分布的东西吗?例如,考虑掷一个骰子。每个数字都有相等的概率,因此实现不遵循正态分布(实际上,他们可能这样做,上周 BBC4 上有一个关于它的节目,它表明彩票球的外观呈正态分布)。但是,如果您掷两个骰子并将它们相加,那么实现确实遵循正态分布。因此,我认为您的计算结果将近似于正态分布,因此找到给定输入集的平均值和 sigma 值就成了问题。您可以计算每个输入的上限和下限以及它们的平均值,我相信一些谷歌搜索会提供将函数应用于正态分布的方法。

我想有一个必然的问题,这就是结果的用途?了解如何使用结果将有助于决定如何创建结果。

【讨论】:

    【解决方案3】:

    忽略程序化解决方案,随着数据集的增长,您可以显着减少添加的总数。

    如果我们定义四个组WXYZ,每个组都包含三个元素,根据您自己的数学计算,这会导致大量运算:

    • W + X => 9 次操作
    • (W + X) + Y => 27 次操作
    • (W + X + Y) + Z => 81 次操作
    • 总计:117 次操作

    但是,如果我们假设您的“添加”操作的定义是严格排序的,因此两组 {a,b}{c,d} 总是导致 {a+c,a+d,b+c,b+d},那么您的操作是 associative .这意味着您可以这样做:

    • W + X => 9 次操作
    • Y + Z => 9 次操作
    • (W + X) + (Y + Z) => 81 次操作
    • 总计:99 次操作

    对于一个简单的例子,这节省了 18 次操作。如果将上述扩展至 6 组,每组 3 名成员,则操作总数可以从 1089 减少到 837 - 几​​乎节省 20%。您拥有的数据越多,这种改进就越明显(更多集合或更多元素将节省更多)。

    此外,这为更好的并行化打开了问题:如果您有 200 个组要处理,您可以先并行组合 100 对,然后是 50 对或结果,然后是 25,等等。这将允许在很大程度上并行性应该会给你更好的性能。 (例如,大约 10 个并行操作将添加 720 个集合,因为每个并行添加将允许将 COUNT 增加 2 倍。)

    我绝对不是这方面的专家,但使用典型 GPU 的并行处理能力似乎是一个理想的问题 - 我的理解是,像 CUDA 这样的东西可以缩短并行处理所有这些计算的工作。

    编辑:如果你真正的问题是“你最大的损失是什么”,那么这是一个容易得多的问题。鉴于最终集合中的每个值都是每个“组件”集合中的一个值的总和,因此您最大的损失通常会通过组合每个组件集合中的 最低 值来找到。找到这些较低的值(每组一个值)是一项简单得多的工作,然后您只需将有限的一组值相加即可。

    【讨论】:

    • 谢谢!我在回答 jt234 的回答时提到了并行性。但是,正是程序化解决方案决定了我是否可以使用 SSE 或 CUDA 之类的东西。
    • 认为我在那里描述的解决方案非常适合 CUDA。但是,如果您所做的只是找到整体范围的下限,我的最后一次编辑也值得考虑。
    • 问题有点复杂。我不是想找出最坏的情况。由于 X 是可能结果的总数,我试图找到 0.01*X 次最差的结果。
    • 好吧,例如,如果您从每个 n 组中取两个最低值,那么您将获得 2^n 最低值(如果不是,您可以取每组中最低的 3 个)足够的)。您可以根据组数和每组的计数轻松计算值的总数。因此,您只需要计算这些较低值的总和即可回答您的问题 - 仍然比计算每个可能的值要容易几个数量级。
    • 丹,我不确定这是否依赖于添加的发行版具有近似相同的范围。想象一下 A={-50K, 10K} 和 B={-10K;40K}。如果您从每个分布中获取 n 最小值并将它们相加,则您无法确定最终得到的是 A+B 分布中的 n^2 最小值。还是我在监督什么?不过感谢您的想法!
    【解决方案4】:

    基本上有两种方法。一个近似值和一个精确值......

    近似法通过大量抽样对随机变量的总和进行建模。基本上,有随机变量AB,我们从每个 r.v. 中随机抽样。 50K 次,添加采样值(这里 SSE 可以提供很大帮助),我们的分布为 A+B。这就是数学家在 Mathematica 中的做法。

    精确方法利用了 Dan Puzey 提出的方法,即仅对每个 r.v. 的密度的一小部分求和。假设我们有具有以下“密度”的随机变量(为简单起见,每个值的可能性相同)

    A = {-5,-3,-2}
    B = {+0,+1,+2}
    C = {+7,+8,+9}
    

    A+B+C 的总和将是

    {2,3,3,4,4,4,4,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,8,8,8,9}
    

    如果我想准确地知道整个分布,我别无选择,只能将 A 的每个元素与 B 的每个元素相加,然后将这个和的每个元素与 C 的每个元素相加。但是,如果我只想要这个总和的 99% VaR,即这个总和的 1%,我只需要将 A,B,C 的最小元素相加。

    更准确地说,我将从每个分布中获取nA,nB,nC 最小的元素。要确定nA,nB,nC,我们首先将它们设置为 1。然后,如果A[nA] = min( A[nA], B[nB], C[nC])nA 加一(计数A,B,C 已排序)。这样,我可以得到A,B,CnA, nB, nC 最小元素,我必须将它们相加(彼此相加)并取第X 个最小总和(其中X 是1% 乘以总和的总组合数, 即A,B,C 为 3*3*3)。这也告诉何时停止增加nA,nB,nC - 当nA*nB*nC > X 时停止。

    但是,像这样,我再次执行相同的冗余操作,即我正在计算 1% 百分位数左侧的 A+B+C 的整个分布。然而,即使这也比计算A+B+C 的整个发行版要短得多。但我相信应该有一个简单的迭代算法来准确地告诉O(a*b) 中的给定 VaR 数,其中a 是添加的 r.v.s 的数量,b 是每个 r.v.s 密度中的最大元素数。

    对于任何关于我是否正确的问题,我都会很高兴。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-13
      • 2011-02-21
      • 2019-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-07
      相关资源
      最近更新 更多