【问题标题】:A grid over probability vectors概率向量上的网格
【发布时间】:2018-11-08 00:26:35
【问题描述】:

我正在尝试获得一个 n 维概率向量的“网格”——每个条目都在 0 和 1 之间的向量,并且所有条目加起来为 1。我希望每个可能的向量都包含其中的坐标可以取 v 个介于 0 和 1 之间的均匀间隔值中的任何一个。

为了说明这一点,下面是一个非常低效的实现,对于 n = 3 和 v = 3:

from itertools import product
grid_redundant = product([0, .5, 1], repeat=3)
grid = [point for point in grid_redundant if sum(point)==1]

现在grid 包含[(0, 0, 1), (0, 0.5, 0.5), (0, 1, 0), (0.5, 0, 0.5), (0.5, 0.5, 0), (1, 0, 0)]

这种“实现”对于更高维度和更细粒度的网格来说非常糟糕。有没有好的方法可以做到这一点,也许使用numpy


我也许可以在动机上加一点:如果只是从随机分布中抽样给我足够的极值点,我会非常高兴,但事实并非如此。见this question。我所追求的“网格”不是随机的,而是系统地扫过单纯形(概率向量的空间)。

【问题讨论】:

  • @yeputons,感谢您的指点。它不是重复的;我已经编辑了这个问题以明确这一点。
  • 关于预先确定的概率值,您还能说些什么?它们只是 [0, 1/(v-1), 2/(v-1), ..., (v-1)/(v-1)]?
  • 是的,抱歉,我考虑了均匀间隔的值。我已编辑问题以反映这一点。

标签: python numpy scientific-computing


【解决方案1】:

这是一个递归解决方案。它不使用 NumPy,也不是超级高效,尽管它应该比发布的 sn-p 更快:

import math
from itertools import permutations

def probability_grid(values, n):
    values = set(values)
    # Check if we can extend the probability distribution with zeros
    with_zero = 0. in values
    values.discard(0.)
    if not values:
        raise StopIteration
    values = list(values)
    for p in _probability_grid_rec(values, n, [], 0.):
        if with_zero:
            # Add necessary zeros
            p += (0.,) * (n - len(p))
        if len(p) == n:
            yield from set(permutations(p))  # faster: more_itertools.distinct_permutations(p)

def _probability_grid_rec(values, n, current, current_sum, eps=1e-10):
    if not values or n <= 0:
        if abs(current_sum - 1.) <= eps:
            yield tuple(current)
    else:
        value, *values = values
        inv = 1. / value
        # Skip this value
        yield from _probability_grid_rec(
            values, n, current, current_sum, eps)
        # Add copies of this value
        precision = round(-math.log10(eps))
        adds = int(round((1. - current_sum) / value, precision))
        for i in range(adds):
            current.append(value)
            current_sum += value
            n -= 1
            yield from _probability_grid_rec(
                values, n, current, current_sum, eps)
        # Remove copies of this value
        if adds > 0:
            del current[-adds:]

print(list(probability_grid([0, 0.5, 1.], 3)))

输出:

[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0), (0.5, 0.5, 0.0), (0.0, 0.5, 0.5), (0.5, 0.0, 0.5)]

与发布方法的快速比较:

from itertools import product

def probability_grid_basic(values, n):
    grid_redundant = product(values, repeat=n)
    return [point for point in grid_redundant if sum(point)==1]

values = [0, 0.25, 1./3., .5, 1]
n = 6
%timeit list(probability_grid(values, n))
1.61 ms ± 20.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit probability_grid_basic(values, n)
6.27 ms ± 186 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

【讨论】:

  • @bobrobbob 好吧,是的,作为一种递归算法,您不需要天文数字大小的输入来破坏它......不过,我不知道 OP 期望的实际大小是多少。 ..
  • 是的 3000 很抱歉。
  • 用更合理的值你的结果是错误的。从 n=v=6 开始,您仅返回 12 个结果(仅与 0/.2/1 的组合),而“基本”返回 252 个结果(0/.2/.4/.6/.8/1 的组合)
  • @bobrobbob 感谢您指出这一点,现在修复它(这是一个浮点精度错误)。
【解决方案2】:

对于高维向量,即使在接受的答案中有聪明的解决方案,完全通用地这样做也是相当难以管理的。在我自己的情况下,计算所有值的相关子集是值得的。例如,以下函数计算所有 dimension 维概率向量,其中只有 n 非零等概率项:

import itertools as it
import numpy as np

def equip_n(dimension, n):
"""
Calculate all possible <dimension>-dimensional probability vectors with n nonzero,
equiprobable entries
"""
combinations  = np.array([comb for comb in it.combinations(range(dimension), n)])
vectors = np.zeros((combinations.shape[0], dimension))
for line, comb in zip(vectors, combinations):
    line[comb] = 1/n
return vectors 

print(equip_n(6, 3))

返回

[[ 0.3333  0.3333  0.3333  0.      0.      0.    ]
 [ 0.3333  0.3333  0.      0.3333  0.      0.    ] 
 [ 0.3333  0.3333  0.      0.      0.3333  0.    ]
 [ 0.3333  0.3333  0.      0.      0.      0.3333]
 [ 0.3333  0.      0.3333  0.3333  0.      0.    ]
 [ 0.3333  0.      0.3333  0.      0.3333  0.    ]
 [ 0.3333  0.      0.3333  0.      0.      0.3333]
 [ 0.3333  0.      0.      0.3333  0.3333  0.    ]
 [ 0.3333  0.      0.      0.3333  0.      0.3333]
 [ 0.3333  0.      0.      0.      0.3333  0.3333]
 [ 0.      0.3333  0.3333  0.3333  0.      0.    ]
 [ 0.      0.3333  0.3333  0.      0.3333  0.    ]
 [ 0.      0.3333  0.3333  0.      0.      0.3333]
 [ 0.      0.3333  0.      0.3333  0.3333  0.    ]
 [ 0.      0.3333  0.      0.3333  0.      0.3333]
 [ 0.      0.3333  0.      0.      0.3333  0.3333]
 [ 0.      0.      0.3333  0.3333  0.3333  0.    ]
 [ 0.      0.      0.3333  0.3333  0.      0.3333]
 [ 0.      0.      0.3333  0.      0.3333  0.3333]
 [ 0.      0.      0.      0.3333  0.3333  0.3333]]

这非常快。 %timeit equip_n(6, 3)返回

15.1 µs ± 74.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

【讨论】:

  • 这不是解决方案。首先,如果 v = 3,则从中选择概率的值应该是 0、0.5 和 1。其次,如果它不包括所有可能的向量,而只包括其中的一些向量。例如 [1, 0, 0, 0, 0, 0]。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-02
  • 1970-01-01
  • 1970-01-01
  • 2021-05-17
  • 2019-06-07
  • 1970-01-01
  • 2021-12-07
相关资源
最近更新 更多