Using numpy I can't completely avoid iteration, but I can limit it to the smallest dimension, the number of possible rotations (8). I've found several alternatives. I suspect the last is fastest, but I haven't done time tests. The core idea is to collect all the possible rotations into an array, and pick the minimum value from those.
x=[[0, 0, 0, 1, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 1],
...
[1, 0, 0, 0, 1, 1, 1, 1]]
x = np.array(x)
M,N = x.shape
j = 2**np.arange(N)[::-1] # powers of 2 used convert vector to number
# use np.dot(xx,j) to produce a base 10 integer
A) In the first version I collect the rotations in a 3D array
xx = np.zeros([M, N, N],dtype=int)
for i in range(N):
xx[:,i,:] = np.roll(x, i, axis=1)
t = np.argmin(np.dot(xx, j), axis=1) # find the mimimum index
print t
print xx[range(M),t,:]
produces:
[4 5 6 0 0 1 4 3 7]
[[0 0 0 0 0 0 0 1]
[0 0 0 0 0 1 0 1]
[0 0 0 0 0 1 1 1]
...
[0 0 0 1 1 1 1 1]]
B) A variation would be to store the np.dot(xx, j) values in a 2D array, and convert the minimum of each row back to the 8 column array.
xx = x.copy()
for i in range(N):
y = np.roll(x, i, axis=1)
xx[:,i] = np.dot(y, j)
y = np.min(xx, axis=1)
print y
# [4 5 6 0 0 1 4 3 7]
# convert back to binary
z = x.copy()
for i in range(N):
z[:,i] = y%2
y = y//2
z = np.fliplr(z)
print z
I couldn't find a numpy way of converting a vector of numbers to a binary array. But with N much smaller than M, this iterative approach isn't costly. numpy.base_repr uses this, but only operates on scalars. [int2array and np.unpackbits used in the other answers are faster.]
C) Better yet, I could roll j rather than x:
xx = x.copy()
for i in range(N):
xx[:,i] = np.dot(x, np.roll(j,i))
y = np.min(xx, axis=1)
print y
D) Possible further speed up by constructing an array of rotated j, and doing the dot product just once. It may be possible to construct jj without iteration, but creating an 8x8 array just once isn't expensive.
jj = np.zeros([N,N], dtype=int)
for i in range(N):
jj[:,i] = np.roll(j,i)
print jj
xx = np.dot(x, jj)
# or xx = np.einsum('ij,jk',x,jj)
y = np.min(xx, axis=1)
print y
Timing notes:
For small x, such as the sample 9 rows, the first solution (A) is fastest. Converting the integers back to binary takes up a 1/3 of time, slowing down the other solutions.
But for a large x, such as 10000 rows, the last is best. With IPython timeit
A) 10 loops, best of 3: 22.7 ms per loop
B) 100 loops, best of 3: 13.5 ms per loop
C) 100 loops, best of 3: 8.21 ms per loop
D) 100 loops, best of 3: 6.15 ms per loop
# Hyperboreous: rotMin(x1)
# adapted to work with numpy arrays
H) 1 loops, best of 3: 177 ms per loop
At one point I thought I might gain speed by selectively rotating rows only until they reach their minimum value. But these added samples show that I cannot use a local minimum:
[1, 0, 1, 0, 1, 0, 0, 0],
[1, 0, 0, 1, 1, 0, 0, 0],
[1, 0, 0, 1, 0, 0, 0, 1],
[0, 1, 0, 1, 0, 1, 0, 0],
[0, 1, 0, 1, 0, 0, 1, 0],
corresponding xx values
[168 81 162 69 138 21 42 84]
[152 49 98 196 137 19 38 76]
[145 35 70 140 25 50 100 200]
[ 84 168 81 162 69 138 21 42]
[ 82 164 73 146 37 74 148 41]
But notice that the minimum for each of these rows is the first 0 of the longest run of 0s. So it might possible to find the minimum without doing all the rotations and conversion to numeric value.
11000001 --> 00000111) correct?