我想迭代大小为1的n
维多维数据集的所有顶点。我知道我可以使用itertools.product
执行此操作,如下所示:
>>> n = 3
>>> for j in it.product((0,1), repeat=n) :
... print j
...
(0, 0, 0)
(0, 0, 1)
(0, 1, 0)
(0, 1, 1)
(1, 0, 0)
(1, 0, 1)
(1, 1, 0)
(1, 1, 1)
但我需要区别对待每个顶点,具体取决于在其坐标中找到的1
的数量,即(0, 1, 1)
,(1, 0, 1)
和(1, 1, 0)
将全部得到相同的tratment,因为他们都有两个1
s。而不是使用上面的迭代器,然后计算1
的数量,我想生成按1
s的数量排序的笛卡尔积,类似于:
>>> for ones in xrange(n) :
... for seq in magic_expression(ones, n) :
... print ones, seq
...
0 (0, 0, 0)
1 (0, 0, 1)
1 (0, 1, 0)
1 (1, 0, 0)
2 (0, 1, 1)
2 (1, 0, 1)
2 (1, 1, 0)
3 (1, 1, 1)
我的高中数学老师会称之为一次取n
个2个元素的排列,其中第一个元素重复n - ones
次,第二个元素重复ones
}次,很容易证明它们有n! / ones! / (n - ones)!
。
根据wikipedia,我可以按字典顺序生成它们,如下所示:
def lexicographical(ones, n) :
perm = [0] * (n - ones) + [1] * ones
yield tuple(perm)
while True :
k = None
for j in xrange(n - 1) :
if perm[j] < perm[j + 1] :
k = j
if k is None :
break
l = k + 1
for j in xrange(k + 2, n) :
if perm[k] < perm[j] :
l = j
perm[k], perm[l] = perm[l], perm[k]
perm[k+1:] = perm[-1:k:-1]
yield tuple(perm)
但要计时,这只能开始反映计算n >= 10
的完整笛卡尔积,然后只计算ones < 2
,这不是典型的用例。是否有一种优雅的方式来加速我的代码,可能是使用一些强大的itertools
巫毒,或者完全使用不同的算法?如果它有所不同,我不会太在意所产生的排列的顺序。或者我应该让自己辞职?
修改 的
我对提出的解决方案做了一些时间安排。按照itertools.product
的顺序使用顶点会生成它们,计数总是最快的。但是为了让它们按照一些数量排序,Eevee对列表进行排序的解决方案对于n <= 6
是最快的,从那时起Cam的解决方案是两者中最快的。
我接受了Cam的解决方案,因为它更好地回复了被问到的问题。但就我将在我的代码中实现的内容而言,我将自己辞职。
答案 0 :(得分:5)
如果您编写了超过8行代码来生成8个常量值,那么就会出现问题。
如果没有嵌入我想要的列表,我会以愚蠢的方式做到:
vertices = (
(v.count(1), v)
for v in itertools.product((0, 1), repeat=3)
)
for count, vertex in sorted(vertices):
print vertex
除非您使用1000超立方体,否则不应该有任何巨大的性能担忧。
答案 1 :(得分:3)
(低效)替代方法:
>>> ['{0:03b}'.format(x) for x in range(8)]
['000', '001', '010', '011', '100', '101', '110', '111']
或者作为元组:
>>> [tuple(int(j) for j in list('{0:03b}'.format(x))) for x in range(8)]
[(0, 0, 0),
(0, 0, 1),
(0, 1, 0),
(0, 1, 1),
(1, 0, 0),
(1, 0, 1),
(1, 1, 0),
(1, 1, 1)]
按顶点数排序:
>>> sorted(_, key=lambda x: sum(x))
[(0, 0, 0),
(0, 0, 1),
(0, 1, 0),
(1, 0, 0),
(0, 1, 1),
(1, 0, 1),
(1, 1, 0),
(1, 1, 1)]
或使用itertools:
>>> sorted(itertools.product((0, 1), repeat=3), key=lambda x: sum(x))
[(0, 0, 0),
(0, 0, 1),
(0, 1, 0),
(1, 0, 0),
(0, 1, 1),
(1, 0, 1),
(1, 1, 0),
(1, 1, 1)]
答案 2 :(得分:2)
对于3d立方体的使用案例,Eevee的解决方案是正确的。
然而,为了好玩并展示itertools的强大功能,这里有一个线性时间解决方案,可以推广到更高的维度:
from itertools import combinations
# n is the number of dimensions of the cube (3 for a 3d cube)
def generate_vertices(n):
for number_of_ones in xrange(0, n + 1):
for location_of_ones in combinations(xrange(0, n), number_of_ones):
result = [0] * n
for location in location_of_ones:
result[location] = 1
yield result
for vertex in generate_vertices(3):
print vertex
# result:
# [0, 0, 0]
# [1, 0, 0]
# [0, 1, 0]
# [0, 0, 1]
# [1, 1, 0]
# [1, 0, 1]
# [0, 1, 1]
# [1, 1, 1]
答案 3 :(得分:1)
根据您对顶点的处理方式进行计数并不是一个坏主意,因为如果必须迭代所有这些顶点,O(f(n))至少为O(f(n)* 2 n),对它们进行排序为O(n * 2 n)。所以它基本上取决于f(n)专业n。
除此之外,这是一个可能的神奇表达:
def magic_expression(ones, n):
a = (0,) * (n - ones) + (1,) * ones
previous = tuple()
for p in itertools.permutations(a):
if p > previous:
previous = p
yield p
在permutations with unique values的帮助下。
这是有效的,因为itertools.permutations会产生排序结果。请注意,a最初是排序的,因为首先是零。
答案 4 :(得分:1)
以下是一些运行速度更快的代码(适用于中等n
),比Cam或Eevee快几倍(适用于大n
)。随后进行时间比较。
def cornersjc (n): # Re: jw code
from itertools import product
m = (n+1)/2
k = n-m
# produce list g of lists of tuples on k bits
g = [[] for i in range(k+1)]
for j in product((0,1), repeat=k):
g[sum(j)].append(tuple(j))
# produce list h of lists of tuples on m bits
if k==m:
h = g
else:
h = [[] for i in range(m+1)]
for j in product((0,1), repeat=m):
h[sum(j)].append(tuple(j))
# Now deliver n-tuples in proper order
for b in range(n+1): # Deliver tuples with b bits set
for lb in range(max(0, b-m), min(b+1,k+1)):
for l in g[lb]:
for r in h[b-lb]:
yield l+r
下面显示的时序结果来自ipython中的一系列%timeit
次调用。每次通话都是一种形式
%timeit [x for x in cube1s.f(n)]
使用cornersjc, cornerscc, cornersec, cornerses
代替f
(代表我的代码,Cam代码,Eevee代码和我的Eevee方法版本)以及代替n
的数字。
n cornersjc cornerscc cornersec cornerses
5 40.3 us 45.1 us 36.4 us 25.2 us
6 51.3 us 85.2 us 77.6 us 46.9 us
7 87.8 us 163 us 156 us 88.4 us
8 132 us 349 us 327 us 178 us
9 250 us 701 us 688 us 376 us
10 437 us 1.43 ms 1.45 ms 783 us
11 873 us 3 ms 3.26 ms 1.63 ms
12 1.87 ms 6.66 ms 8.34 ms 4.9 ms
上面给出了cornersjc
的代码。 cornerscc
,cornersec
和cornerses
的代码如下。这些产生与cornersjc
相同的输出,除了Cam的代码产生列表而不是元组列表,并且每个位计数组内产生相反的结果。
def cornerscc(n): # Re: Cam's code
from itertools import combinations
for number_of_ones in xrange(0, n + 1):
for location_of_ones in combinations(xrange(0, n), number_of_ones):
result = [0] * n
for location in location_of_ones:
result[location] = 1
yield result
def cornersec (n): # Re: Eevee's code
from itertools import product
vertices = ((v.count(1), v)
for v in product((0, 1), repeat=n))
for count, vertex in sorted(vertices):
yield vertex
def cornerses (n): # jw mod. of Eevee's code
from itertools import product
for vertex in sorted(product((0, 1), repeat=n), key=sum):
yield vertex
注意,cornersjc
的最后三行可以替换为
for v in product(g[lb], h[b-lb]):
yield v[0]+v[1]
更干净但更慢。请注意,如果使用的是yield v
而不是yield v[0]+v[1]
,则代码的运行速度会快于cornersjc
,但(在n=5
处)会产生一对元组结果,如((1,0) ),(1,1,0));当使用yield v[0]+v[1]
时,代码运行速度比cornersjc
慢,但产生相同的结果,元组列表如(1,0,1,1,0)。
下面是一个示例时间,cornersjp
是修改后的cornersjc
。
In [93]: for n in range(5,13):
%timeit [x for x in cube1s.cornersjp(n)]
....:
10000 loops, best of 3: 49.3 us per loop
10000 loops, best of 3: 64.9 us per loop
10000 loops, best of 3: 117 us per loop
10000 loops, best of 3: 178 us per loop
1000 loops, best of 3: 351 us per loop
1000 loops, best of 3: 606 us per loop
1000 loops, best of 3: 1.28 ms per loop
100 loops, best of 3: 2.74 ms per loop