I want to iterate over all the vertices of an n
dimensional cube of size 1. I know I could do that with itertools.product
as follows:
&
Following is some code that runs faster (for medium n
) and several times faster (for large n
) than that of Cam or Eevee. A time comparison follows.
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
The timing results shown below are from a series of %timeit
calls in ipython. Each call was of a form like
%timeit [x for x in cube1s.f(n)]
with the names cornersjc, cornerscc, cornersec, cornerses
in place of f
(standing for my code, Cam's code, Eevee's code, and my version of Eevee's method) and a number in place of 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
Code for cornersjc
was given above. Code for cornerscc
, cornersec
, and cornerses
is as follows. These produce the same output as cornersjc
, except that Cam's code produces a list of lists instead of a list of tuples, and within each bit-count group produces in reverse.
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
Note, the last three lines of cornersjc
can be replaced by
for v in product(g[lb], h[b-lb]):
yield v[0]+v[1]
which is cleaner but slower. Note, if yield v
is used instead of yield v[0]+v[1]
, the code runs faster than cornersjc
but (at n=5
) produces pair-of-tuple results like ((1, 0), (1, 1, 0)); when yield v[0]+v[1]
is used, the code runs slower than cornersjc
but produces identical results, a list of tuples like (1, 0, 1, 1, 0).
An example timing follows, with cornersjp
being the modified 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