Python 如何索引笛卡尔积

Python 如何索引笛卡尔积,python,Python,假设变量x和theta可以分别取可能的值[0,1,2]和[0,1,2,3] 假设在一个实现中,x=1和theta=3。表示这一点的自然方式是使用元组(1,3)。但是,我想用一个索引来标记state(1,3)。一种“蛮力”方法是形成所有可能的有序对的笛卡尔积(x,θ),并进行查找: import numpy as np import itertools N_x = 3 N_theta = 4 np.random.seed(seed = 1) x = np.random.choice(range

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

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

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,θ)=(1,3)
box=7
,如果我们在
状态列表中查找它,这是有意义的:

[(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_x
N_theta
的状态数可能会有所不同,笛卡尔乘积中可能有更多的变量).

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

index = x * N_theta + theta
其中
(x,θ)
是元组之一

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

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

这可能不是最具python风格的方法,但它显示了正在发生的事情:您将元组视为一个超立方体,其中只能沿着一个维度移动,但如果到达边缘,“下一个”维度中的坐标将增加,移动坐标将重置。建议读者画一些画

如果您总是按字典顺序存储
状态
,并且
x
theta
的可能值总是从
0
到您的示例所示的某个最大值的完整范围,那么您可以使用以下公式

index = x * N_theta + theta
其中
(x,θ)
是元组之一

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

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

这可能不是最具python风格的方法,但它显示了正在发生的事情:您将元组视为一个超立方体,其中只能沿着一个维度移动,但如果到达边缘,“下一个”维度中的坐标将增加,移动坐标将重置。建议读者画一些画

我想这取决于你掌握的数据。如果它们是稀疏的,最好的解决方案是使用字典。并适用于任何元组的维度

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)

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

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

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)

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

为了完整起见,我将包括我的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 method yields 23
Analytical method yields 23

但我预计“分析”方法会更快。根据Julian的建议,我已将表示形式更改为
p
N

为了完整起见,我将包括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 method yields 23
Analytical method yields 23

但我预计“分析”方法会更快。我已经按照Julian的建议将表示形式更改为
p
N

您可以使用哈希寻址,将这两个组件模化为一个大常量,然后在发生冲突的情况下附加到每个哈希键后面的列表中。因此,例如c2*(x%c1)+(y%c2)将是散列键。您可以使用散列寻址,将两个组件模化为一个大常量,然后在发生冲突时附加到每个散列键后面的列表中。例如c2*(x%c1)+(y%c2)将是散列键。我考虑过这一点,但它如何扩展到更多变量?例如,除了
x
theta
,还有
x\u点和
theta\u点
。我考虑过这一点,但它如何扩展到更多变量?例如,除了
x
theta
,还可以有
x\u点和
theta\u点。