【问题标题】:How to index a Cartesian product如何索引笛卡尔积
【发布时间】:2016-08-04 19:16:00
【问题描述】:

假设变量xtheta 可以分别取可能的值[0, 1, 2][0, 1, 2, 3]

假设在一个实现中,x = 1theta = 3。表示这一点的自然方式是使用元组 (1,3)。但是,我想用单个索引标记状态 (1,3)。这样做的一种“蛮力”方法是形成所有可能的有序对(x,theta) 的笛卡尔积并进行查找:

import numpy as np
import itertools

N_x = 3
N_theta = 4

np.random.seed(seed = 1)
x = np.random.choice(range(N_x))
theta = np.random.choice(range(N_theta))

def get_box(x, N_x, theta, N_theta):
    states = list(itertools.product(range(N_x),range(N_theta)))
    inds = [i for i in range(len(states)) if states[i]==(x,theta)]
    return inds[0]

print (x, theta)
box = get_box(x, N_x, theta, N_theta)
print box

这给出了(x, theta) = (1,3)box = 7,如果我们在states 列表中查找它是有意义的:

[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)]

但是,这种“蛮力”方法似乎效率低下,因为应该可以预先确定索引而无需查找它。有什么通用的方法可以做到这一点吗? (N_xN_theta的状态数在实际应用中可能会有所不同,笛卡尔积可能会有更多变量)。

【问题讨论】:

  • 您可以使用哈希寻址,将两个组件取模一个大常数,然后在发生冲突时附加到每个哈希键后面的列表中。因此,例如 c2 * (x % c1) + (y % c2) 将是哈希键。

标签: python


【解决方案1】:

如果您始终按字典顺序存储states,并且xtheta 的可能值始终是从0 到某个最大值的完整范围,如您的示例所示,您可以使用公式

index = x * N_theta + theta

(x, theta) 是您的元组之一。

这通过以下方式推广到高维元组:如果N 是表示变量范围的列表或元组(因此N[0] 是第一个变量的可能值的数量等)和@ 987654329@ 是一个元组,您可以使用以下 sn-p 将索引放入所有可能元组的字典排序列表中:

index = 0
skip = 1
for dimension in reversed(range(len(N))):
    index += skip * p[dimension]
    skip *= N[dimension]

这可能不是最 Pythonic 的方法,但它显示了正在发生的事情:您将元组视为一个超立方体,您只能沿着一个维度前进,但如果您到达边缘,您的坐标在“下一个”维度增加,您的旅行坐标重置。建议读者画一些图。 ;)

【讨论】:

  • 我考虑过这个,但它如何扩展到更多变量?例如,除了xtheta,还可以有x_dottheta_dot
【解决方案2】:

我认为这取决于您拥有的数据。如果它们是稀疏的,最好的解决方案是字典。适用于任何元组的维度。

import itertools
import random

n = 100
m = 100
l1 = [i for i in range(n)]
l2 = [i for i in range(m)]

a = {}
prod = [element for element in itertools.product(l1, l2)]
for i in prod:
    a[i] = random.randint(1, 100)

关于性能的一个很好的来源是this discution

【讨论】:

    【解决方案3】:

    为了完整起见,我将包括我对 Julian Kniephoff 解决方案 get_box3 的实现,以及对原始实现 get_box2 的略微修改版本:

    # 'Brute-force' method
    def get_box2(p, N):
        states = list(itertools.product(*[range(n) for n in N]))
        return states.index(p)
    
    # 'Analytic' method
    def get_box3(p, N):
        index = 0
        skip = 1
        for dimension in reversed(range(len(N))):
            index += skip * p[dimension]
            skip *= N[dimension]
        return index
    
    p = (1,3,2)         # Tuple characterizing the total state of the system
    N = [3,4,3]         # List of the number of possible values for each state variable
    
    print "Brute-force method yields %s" % get_box2(p, N)
    print "Analytical method yields %s" % get_box3(p, N)
    

    'brute-force' 和 'analytic' 方法产生相同的结果:

    Brute-force method yields 23
    Analytical method yields 23
    

    但我希望“分析”方法更快。根据 Julian 的建议,我已将表示更改为 pN

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-06
      • 2017-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-15
      • 2020-06-01
      相关资源
      最近更新 更多