生成总和为给定数字并符合一组一般约束的随机自然数
Generate random natural numbers that sum to a given number and comply to a set of general constraints
我有一个应用程序需要类似于 here 中描述的问题。
我也需要生成一组加起来等于给定总和 S 的正整数随机变量 {Xi},其中每个变量可能具有约束条件,例如 mi<=Xi<=Mi。
我知道该怎么做,问题是在我的情况下,我也可能 随机变量本身之间存在约束 ,比如 Xi<=Fi(Xj) 对于一些给定 Fi(也可以说 Fi 的逆已知),Now,应该如何“正确”生成随机变量?我在这里正确地加了引号,因为我不太确定它在这里意味着什么,只是我希望生成的数字能够涵盖所有可能的情况,并且对每种可能的情况都具有尽可能统一的概率。
假设我们甚至看一个非常简单的案例:
4个随机变量X1,X2,X3,X4需要加起来为100并遵守约束条件X1 <= 2*X2,what将是生成它们的“正确”方式吗?
P.S。我知道这似乎更适合数学溢出,但我也没有找到解决方案。
对于4个随机变量X1,X2,X3,X4需要加起来为100且满足约束条件X1 <= 2*X2,可以使用multinomial distribution
只要第一个数字的概率足够低,你的
几乎总是会满足条件,如果不满足 - 拒绝并重复。
设计的多项式分布总和等于 100。
代码,Windows10 x64,Python3.8
import numpy as np
def x1x2x3x4(rng):
while True:
v = rng.multinomial(100, [0.1, 1/2-0.1, 1/4, 1/4])
if v[0] <= 2*v[1]:
return v
return None
rng = np.random.default_rng()
print(x1x2x3x4(rng))
print(x1x2x3x4(rng))
print(x1x2x3x4(rng))
更新
选择概率的自由度很高。例如,您可以使其他 (##2, 3, 4) 对称。代码
def x1x2x3x4(rng, pfirst = 0.1):
pother = (1.0 - pfirst)/3.0
while True:
v = rng.multinomial(100, [pfirst, pother, pother, pother])
if v[0] <= 2*v[1]:
return v
return None
更新二
如果您开始拒绝组合,那么您会人为地增加一个事件子集的概率并降低另一组事件的概率 - 总和始终为 1。没有办法在您想要的条件下获得统一的概率遇到。下面的代码以等概率运行多项式并计算直方图和平均值。平均值应该正好是 25 (=100/4),但是一旦您拒绝了一些样本,您就会降低第一个值的平均值并增加第二个值的平均值。差异很小,但不可避免。如果你觉得没问题,那就这样吧。代码
import numpy as np
import matplotlib.pyplot as plt
def x1x2x3x4(rng, summa, pfirst = 0.1):
pother = (1.0 - pfirst)/3.0
while True:
v = rng.multinomial(summa, [pfirst, pother, pother, pother])
if v[0] <= 2*v[1]:
return v
return None
rng = np.random.default_rng()
s = 100
N = 5000000
# histograms
first = np.zeros(s+1)
secnd = np.zeros(s+1)
third = np.zeros(s+1)
forth = np.zeros(s+1)
mfirst = np.float64(0.0)
msecnd = np.float64(0.0)
mthird = np.float64(0.0)
mforth = np.float64(0.0)
for _ in range(0, N): # sampling with equal probabilities
v = x1x2x3x4(rng, s, 0.25)
q = v[0]
mfirst += np.float64(q)
first[q] += 1.0
q = v[1]
msecnd += np.float64(q)
secnd[q] += 1.0
q = v[2]
mthird += np.float64(q)
third[q] += 1.0
q = v[3]
mforth += np.float64(q)
forth[q] += 1.0
x = np.arange(0, s+1, dtype=np.int32)
fig, axs = plt.subplots(4)
axs[0].stem(x, first, markerfmt=' ')
axs[1].stem(x, secnd, markerfmt=' ')
axs[2].stem(x, third, markerfmt=' ')
axs[3].stem(x, forth, markerfmt=' ')
plt.show()
print((mfirst/N, msecnd/N, mthird/N, mforth/N))
打印
(24.9267492, 25.0858356, 24.9928602, 24.994555)
注意!正如我所说,第一个平均值较低,第二个平均值较高。直方图也有点不同
更新三
好吧,狄利克雷,就这样吧。让我们计算过滤器前后生成器的平均值。代码
import numpy as np
def generate(n=10000):
uv = np.hstack([np.zeros([n, 1]),
np.sort(np.random.rand(n, 2), axis=1),
np.ones([n,1])])
return np.diff(uv, axis=1)
a = generate(1000000)
print("Original Dirichlet sample means")
print(a.shape)
print(np.mean((a[:, 0] * 100).astype(int)))
print(np.mean((a[:, 1] * 100).astype(int)))
print(np.mean((a[:, 2] * 100).astype(int)))
print("\nFiltered Dirichlet sample means")
q = (a[(a[:,0]<=2*a[:,1]) & (a[:,2]>0.35),:] * 100).astype(int)
print(q.shape)
print(np.mean(q[:, 0]))
print(np.mean(q[:, 1]))
print(np.mean(q[:, 2]))
我有
Original Dirichlet sample means
(1000000, 3)
32.833758
32.791228
32.88054
Filtered Dirichlet sample means
(281428, 3)
13.912784086871243
28.36360987535
56.23109285501087
你看出区别了吗?一旦应用任何类型的过滤器,就会改变分布。没有什么是统一的了
好的,所以我有这个解决方案来解决我的实际问题,我生成 9000 个 3 个随机变量的三元组,方法是将零连接到已排序的随机元组数组,最后连接到一个,然后按照 the answer on SO I mentioned in my original question 中的建议获取它们的差异。
然后我简单地过滤掉那些不符合我的约束条件的并绘制它们。
S = 100
def generate(n=9000):
uv = np.hstack([np.zeros([n, 1]),
np.sort(np.random.rand(n, 2), axis=1),
np.ones([n,1])])
return np.diff(uv, axis=1)
a = generate()
def plotter(a):
fig = plt.figure(figsize=(10, 10), dpi=100)
ax = fig.add_subplot(projection='3d')
surf = ax.scatter(*zip(*a), marker='o', color=a / 100)
ax.view_init(elev=25., azim=75)
ax.set_xlabel('$A_1$', fontsize='large', fontweight='bold')
ax.set_ylabel('$A_2$', fontsize='large', fontweight='bold')
ax.set_zlabel('$A_3$', fontsize='large', fontweight='bold')
lim = (0, S);
ax.set_xlim3d(*lim);
ax.set_ylim3d(*lim);
ax.set_zlim3d(*lim)
plt.show()
b = a[(a[:, 0] <= 3.5 * a[:, 1] + 2 * a[:, 2]) &\
(a[:, 1] >= (a[:, 2])),:] * S
plotter(b.astype(int))
如您所见,分布均匀分布在单纯形的这些任意限制上,但我仍然不确定我是否可以放弃不遵守约束的样本(以某种方式将约束处理成生成过程?我现在几乎可以肯定它不能为一般的 {Fi} 完成)。这在一般情况下可能很有用,如果您的约束将采样区域限制为整个单纯形的一个非常小的子区域(因为像这样重新采样意味着要从受限区域采样,您需要从单纯形中采样 1/一次)。
如果有人对最后一个问题有答案,我将不胜感激(会将所选答案更改为他的答案)。
我有一个问题的答案,在一般的约束条件下,我所做的是:
- 对约束进行采样以评估约束区域 s。
- 如果 s 足够大,则生成随机样本并丢弃那些不符合我之前回答中描述的约束的样本。
- 否则:
- 枚举整个单纯形。
- 应用约束以过滤掉约束区域外的所有元组。
- 列出过滤后的元组。
- 要求生成,我是从这个结果列表中统一选择生成的。
(注意:这值得我努力只是因为我经常被要求生成)
- 这两种策略的组合应涵盖大多数情况。
注意:我还必须处理 S 是随机生成的参数 (m < S < M) 的情况,在这种情况下,我只是将其视为另一个约束在 m 和 M 之间的随机变量,并将它与其余变量并按照我之前的描述进行处理。
我有一个应用程序需要类似于 here 中描述的问题。
我也需要生成一组加起来等于给定总和 S 的正整数随机变量 {Xi},其中每个变量可能具有约束条件,例如 mi<=Xi<=Mi。
我知道该怎么做,问题是在我的情况下,我也可能 随机变量本身之间存在约束 ,比如 Xi<=Fi(Xj) 对于一些给定 Fi(也可以说 Fi 的逆已知),Now,应该如何“正确”生成随机变量?我在这里正确地加了引号,因为我不太确定它在这里意味着什么,只是我希望生成的数字能够涵盖所有可能的情况,并且对每种可能的情况都具有尽可能统一的概率。
假设我们甚至看一个非常简单的案例: 4个随机变量X1,X2,X3,X4需要加起来为100并遵守约束条件X1 <= 2*X2,what将是生成它们的“正确”方式吗?
P.S。我知道这似乎更适合数学溢出,但我也没有找到解决方案。
对于4个随机变量X1,X2,X3,X4需要加起来为100且满足约束条件X1 <= 2*X2,可以使用multinomial distribution
只要第一个数字的概率足够低,你的 几乎总是会满足条件,如果不满足 - 拒绝并重复。 设计的多项式分布总和等于 100。
代码,Windows10 x64,Python3.8
import numpy as np
def x1x2x3x4(rng):
while True:
v = rng.multinomial(100, [0.1, 1/2-0.1, 1/4, 1/4])
if v[0] <= 2*v[1]:
return v
return None
rng = np.random.default_rng()
print(x1x2x3x4(rng))
print(x1x2x3x4(rng))
print(x1x2x3x4(rng))
更新
选择概率的自由度很高。例如,您可以使其他 (##2, 3, 4) 对称。代码
def x1x2x3x4(rng, pfirst = 0.1):
pother = (1.0 - pfirst)/3.0
while True:
v = rng.multinomial(100, [pfirst, pother, pother, pother])
if v[0] <= 2*v[1]:
return v
return None
更新二
如果您开始拒绝组合,那么您会人为地增加一个事件子集的概率并降低另一组事件的概率 - 总和始终为 1。没有办法在您想要的条件下获得统一的概率遇到。下面的代码以等概率运行多项式并计算直方图和平均值。平均值应该正好是 25 (=100/4),但是一旦您拒绝了一些样本,您就会降低第一个值的平均值并增加第二个值的平均值。差异很小,但不可避免。如果你觉得没问题,那就这样吧。代码
import numpy as np
import matplotlib.pyplot as plt
def x1x2x3x4(rng, summa, pfirst = 0.1):
pother = (1.0 - pfirst)/3.0
while True:
v = rng.multinomial(summa, [pfirst, pother, pother, pother])
if v[0] <= 2*v[1]:
return v
return None
rng = np.random.default_rng()
s = 100
N = 5000000
# histograms
first = np.zeros(s+1)
secnd = np.zeros(s+1)
third = np.zeros(s+1)
forth = np.zeros(s+1)
mfirst = np.float64(0.0)
msecnd = np.float64(0.0)
mthird = np.float64(0.0)
mforth = np.float64(0.0)
for _ in range(0, N): # sampling with equal probabilities
v = x1x2x3x4(rng, s, 0.25)
q = v[0]
mfirst += np.float64(q)
first[q] += 1.0
q = v[1]
msecnd += np.float64(q)
secnd[q] += 1.0
q = v[2]
mthird += np.float64(q)
third[q] += 1.0
q = v[3]
mforth += np.float64(q)
forth[q] += 1.0
x = np.arange(0, s+1, dtype=np.int32)
fig, axs = plt.subplots(4)
axs[0].stem(x, first, markerfmt=' ')
axs[1].stem(x, secnd, markerfmt=' ')
axs[2].stem(x, third, markerfmt=' ')
axs[3].stem(x, forth, markerfmt=' ')
plt.show()
print((mfirst/N, msecnd/N, mthird/N, mforth/N))
打印
(24.9267492, 25.0858356, 24.9928602, 24.994555)
注意!正如我所说,第一个平均值较低,第二个平均值较高。直方图也有点不同
更新三
好吧,狄利克雷,就这样吧。让我们计算过滤器前后生成器的平均值。代码
import numpy as np
def generate(n=10000):
uv = np.hstack([np.zeros([n, 1]),
np.sort(np.random.rand(n, 2), axis=1),
np.ones([n,1])])
return np.diff(uv, axis=1)
a = generate(1000000)
print("Original Dirichlet sample means")
print(a.shape)
print(np.mean((a[:, 0] * 100).astype(int)))
print(np.mean((a[:, 1] * 100).astype(int)))
print(np.mean((a[:, 2] * 100).astype(int)))
print("\nFiltered Dirichlet sample means")
q = (a[(a[:,0]<=2*a[:,1]) & (a[:,2]>0.35),:] * 100).astype(int)
print(q.shape)
print(np.mean(q[:, 0]))
print(np.mean(q[:, 1]))
print(np.mean(q[:, 2]))
我有
Original Dirichlet sample means
(1000000, 3)
32.833758
32.791228
32.88054
Filtered Dirichlet sample means
(281428, 3)
13.912784086871243
28.36360987535
56.23109285501087
你看出区别了吗?一旦应用任何类型的过滤器,就会改变分布。没有什么是统一的了
好的,所以我有这个解决方案来解决我的实际问题,我生成 9000 个 3 个随机变量的三元组,方法是将零连接到已排序的随机元组数组,最后连接到一个,然后按照 the answer on SO I mentioned in my original question 中的建议获取它们的差异。
然后我简单地过滤掉那些不符合我的约束条件的并绘制它们。
S = 100
def generate(n=9000):
uv = np.hstack([np.zeros([n, 1]),
np.sort(np.random.rand(n, 2), axis=1),
np.ones([n,1])])
return np.diff(uv, axis=1)
a = generate()
def plotter(a):
fig = plt.figure(figsize=(10, 10), dpi=100)
ax = fig.add_subplot(projection='3d')
surf = ax.scatter(*zip(*a), marker='o', color=a / 100)
ax.view_init(elev=25., azim=75)
ax.set_xlabel('$A_1$', fontsize='large', fontweight='bold')
ax.set_ylabel('$A_2$', fontsize='large', fontweight='bold')
ax.set_zlabel('$A_3$', fontsize='large', fontweight='bold')
lim = (0, S);
ax.set_xlim3d(*lim);
ax.set_ylim3d(*lim);
ax.set_zlim3d(*lim)
plt.show()
b = a[(a[:, 0] <= 3.5 * a[:, 1] + 2 * a[:, 2]) &\
(a[:, 1] >= (a[:, 2])),:] * S
plotter(b.astype(int))
如您所见,分布均匀分布在单纯形的这些任意限制上,但我仍然不确定我是否可以放弃不遵守约束的样本(以某种方式将约束处理成生成过程?我现在几乎可以肯定它不能为一般的 {Fi} 完成)。这在一般情况下可能很有用,如果您的约束将采样区域限制为整个单纯形的一个非常小的子区域(因为像这样重新采样意味着要从受限区域采样,您需要从单纯形中采样 1/一次)。
如果有人对最后一个问题有答案,我将不胜感激(会将所选答案更改为他的答案)。
我有一个问题的答案,在一般的约束条件下,我所做的是:
- 对约束进行采样以评估约束区域 s。
- 如果 s 足够大,则生成随机样本并丢弃那些不符合我之前回答中描述的约束的样本。
- 否则:
- 枚举整个单纯形。
- 应用约束以过滤掉约束区域外的所有元组。
- 列出过滤后的元组。
- 要求生成,我是从这个结果列表中统一选择生成的。 (注意:这值得我努力只是因为我经常被要求生成)
- 这两种策略的组合应涵盖大多数情况。
注意:我还必须处理 S 是随机生成的参数 (m < S < M) 的情况,在这种情况下,我只是将其视为另一个约束在 m 和 M 之间的随机变量,并将它与其余变量并按照我之前的描述进行处理。