【问题标题】:Sample two random variables uniformly, in region where sum is greater than zero在总和大于零的区域对两个随机变量进行均匀采样
【发布时间】:2021-07-13 05:30:57
【问题描述】:

我试图弄清楚如何在两者之和大于零的区域中对两个随机变量进行均匀采样。我认为一个解决方案可能是为X~U(-1,1) 采样,然后为Y~U(-x,1) 采样,其中x 将是X 的当前采样。

但这导致了一个看起来像这样的分布。

这看起来并不均匀分布,因为左上角的点密度更高,并且随着我们向右移动而不断减少。有人能指出我推理的缺陷在哪里以及如何解决这个问题吗?

谢谢

【问题讨论】:

  • 从您的图中可以看出,在感兴趣的区域中,x=+0.75 比 x=-0.75 更“可能”。因此,您的“x”不是均匀分布的。所以你不应该通过均匀分布对 x 进行采样。对 x 的正确分布进行采样的一般方法是Inverse Method。但我不确定在你的问题中打破 x 和 y 之间的对称性是一件好事。通常,您倾向于使用问题中的对称性。

标签: random uniform-distribution


【解决方案1】:

您只需要确保适当地调整远离“左上”角的 x 点的密度。我还建议在 [0,1] 中生成,然后再转换为 [-1,1]。

例如:

import numpy as np

# generate points, sqrt takes care of moving points away from zero
n = 50000
x = np.sqrt(np.random.uniform(size=n))
y = np.random.uniform(1-x)

# transform to -1,1
x = x * 2 - 1
y = y * 2 - 1

绘制这些给出:

这对我来说看起来很合理。请注意,我已经为 [-1,1] 方块着色以显示它应该适合的位置。

【讨论】:

  • 感谢您的回答!我试图将此视为一种练习,以更好地理解采样。您能否详细说明一下您是如何得出答案的?
  • @krishna 在 [0,1] 中工作使更多的数学函数自然地做“正确的事情”。对于平方根的使用,请注意在 x 轴的一半处,y 轴上有一半的空间。我刚刚找到cs.cmu.edu/~nasmith/papers/smith+tromble.tr04.pdf,这表明我可能没有做正确的事情,但上面的情节对我来说很有说服力,而且肯定比你所拥有的更好
【解决方案2】:
您能否详细说明您是如何得出答案的?

嗯,主要问题在于获得一种公平的方式来采样坐标 X 的非均匀分布。

从初等几何来看,上三角形x0部分的面积为:(1/2) * (x0 + 1)2。由于这个上三角形的总面积等于 2,因此上三角形内 (X 0) 的累积概率 P 为:P = (1/4) * (x 0 + 1)2.

所以,倒置最后一个公式,我们有:x0 = 2*sqrt(P) - 1

现在,根据Inverse Transform Sampling 定理,我们知道我们可以通过重新解释 P 作为随机变量 U0公平抽样 /sub> 均匀分布在 0 和 1 之间。

在 Python 中,这给了我们:

    u0 = random.uniform(0.0, 1.0)
    x = (2*math.sqrt(u0)) - 1.0

或等效:

    u0 = random.random()
    x  = (2 * math.sqrt(u0)) - 1.0

请注意,这与@SamMason 的出色答案基本相同。那件事来自一般统计原理。它也可以用来证明 3D 球体上纬度的公平采样由 arcsin(2*u - 1) 给出。

所以现在我们有了 x,但我们仍然需要 y。底层的二维密度是均匀的,因此对于给定的 x,y 的所有可能值都是均匀分布的。

y 的可能值区间为 [-x, 1]。因此,如果 U1 是另一个均匀分布在 0 和 1 之间的独立随机变量,则可以从等式中得出 y:

y = (1+x) * u1 - x

在 Python 中由以下方式渲染:

    u1 = random.random()
    y  = (1+x)*u1 - x

总的来说,Python代码可以这样写:

import  math
import  random
import  matplotlib.pyplot  as  plt

def mySampler():
    u0 = random.random()
    u1 = random.random()
    x  = 2*math.sqrt(u0) - 1.0
    y  = (1+x)*u1 - x
    return (x,y)

#--- Main program:

points = (mySampler()  for _ in range(10000))  # an iterator object

xx, yy = zip(*points)

plt.scatter(xx, yy, s=0.2)
plt.show()

从图形上看,结果看起来足够好:

旁注:更便宜的临时解决方案:

总是有可能在整个正方形中均匀采样,而拒绝 x+y 和恰好为负的点。但这有点浪费。通过注意“坏”区域与“好”区域具有相同的形状和面积,我们可以得到一个更优雅的解决方案。

所以如果我们得到一个“坏”点,而不是仅仅拒绝它,我们可以用它关于 x+y=0 分割线的对称点来替换它。这可以使用以下 Python 代码完成:

def mySampler2():
    x0 = random.uniform(-1.0, 1.0)
    y0 = random.uniform(-1.0, 1.0)
    s  = x0+y0
    if (s >= 0):
      return (x0, y0)       # good point
    else:
      return (x0-s, y0-s)   # symmetric of bad point

这也很好用。这可能是关于 CPU 时间的最便宜的解决方案,因为我们不拒绝任何内容,而且我们不需要计算平方根。

【讨论】:

  • 比我的手波解释好多了!此外,镜像被拒绝的样本很整洁,我以前总是很难拒绝变量。由于缺少不可预测的分支,使用sqrt 可能会更快,但是您可以再次重新排列拒绝采样器以对分支预测器更友好
【解决方案3】:

关注Generate random locations within a triangular domain

代码,在任何三角形中均匀采样,Python 3.9.4,Win 10 x64

import math
import random

import matplotlib.pyplot as plt

def trisample(A, B, C):
    """
    Given three vertices A, B, C,
    sample point uniformly in the triangle
    """
    r1 = random.random()
    r2 = random.random()

    s1 = math.sqrt(r1)

    x = A[0] * (1.0 - s1) + B[0] * (1.0 - r2) * s1 + C[0] * r2 * s1
    y = A[1] * (1.0 - s1) + B[1] * (1.0 - r2) * s1 + C[1] * r2 * s1

    return (x, y)

random.seed(312345)
A = (1, 0)
B = (1, 1)
C = (0, 1)
points = [trisample(A, B, C) for _ in range(10000)]

xx, yy = zip(*points)
plt.scatter(xx, yy, s=0.2)
plt.show()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-26
    • 2012-10-12
    • 2013-11-19
    • 1970-01-01
    • 2014-08-22
    • 2019-07-29
    • 2018-03-31
    相关资源
    最近更新 更多