在总和大于零的区域均匀采样两个随机变量

Sample two random variables uniformly, in region where sum is greater than zero

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

的当前样本

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

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

谢谢

你只需要确保适当地调整远离“左上”角的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] 正方形着色以显示它应该适合的位置。

关注Generate random locations within a triangular domain

代码,任意三角形均匀采样,Python3.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()

Could you please elaborate a bit on how you arrived at the answer?

好吧,主要问题在于获得一种公平的方法来对坐标 X 的 非均匀 分布进行采样。

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

因此,将上一个公式反转,我们有:x0 = 2*sqrt(P) - 1

现在,根据 Inverse Transform Sampling 定理,我们知道我们可以通过 重新解释 P 生成 X 的 公平采样 作为随机变量 U0 在 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 的出色回答中的数学本质上是相同的。那东西来自于一个general统计原理。它也可以用来证明 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 时间的最便宜的解决方案,因为我们什么都不拒绝,我们不需要计算平方根。