2

I have a numpy.array and would like to rotate its content one bit to the right. I want to perform this as efficient (in terms of execution speed) as possible. Also, please note that every element of the array is an 8-bit number (np.uint8). The rotation assumes that the array stores one big number which is split into chunks of size 8-bit, i.e., I'm not interested in rotating every 8-bit element by itself, but the whole array together.

Here is an example to remove any confusion:

a = numpy.array([0b00000000, 0b00000001])
# rotation should be performed globally
# i.e., the result should be
# rotate(a) == numpy.array([0b10000000, 0b00000000])

How I tried solving the problem?

Method #1: Convert the input array to binary representation and catenate the binary strings of the elements into one big string. Then pop the least significant bit and insert it ahead of the most significant bit. Finally, chop the big string into 8-bit chunks, convert every chunk into np.uint8, and store it in the corresponding position of the rotation result. I guess this solution is correct, but not efficient, especially if the input array is huge.

Method #2: I found it hard to explain the idea in words, so I'll just try convey it using the code fragment below:

# Let w be the input array
# read the least significant bits of every element in w 
# and store them into another array lsb
mask = np.ones(shape=w.shape, dtype=np.int8)
lsb = np.bitwise_and(w,mask)
shiftedLsb = np.left_shift(np.roll(lsb, 1), 7)
rotW = np.right_shift(w,1) 
rotationResult = np.bitwise_or(shiftedLsb, rotw)

My question: Is there a better way, in terms of execution speed, to implement this kind of rotation?

Thank you all.

2 Answers 2

2

You can speed up your "Method #2" by reducing the amount of memory allocation for temporaries:

def method2a(w):
    rotW = np.right_shift(w, 1)
    lsb = np.bitwise_and(w, 1)
    np.left_shift(lsb, 7, lsb)
    rotW[0] |= lsb[-1]
    rotW[1:] |= lsb[:-1]
    return rotW

On my system, with a 1MB input array, this is twice as fast as your original, and produces the same results.

If you're willing to destroy the input, you could eliminate one of the two remaining allocations (perhaps by adding an optional out argument, as NumPy does in e.g. left_shift()).

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. It took me a while to understand this line of yours rotW[1:] |= lsb[:-1] (so clever). As I understand, unnecessary memory allocations should be avoided if a fast execution is required.
0

Came across this answer while searching for a similar thing. Here's what I did for the same thing. Thought of posting in case someone would like copy/paste :-)

# Function to rotate to right(MSB--> LSB, and extracted bit to MSB) a single 8-bit int or a numpy array of such ints by 'positions' positions.
# Inner 'and' extracts LSB bits, the left_shift moves them to MSB and then 'or's them to the remining right shifted MSB bits. 
# The 2**n -1 term produces '111' bits of 'n' length to 'and' them to original number to extract LSB. e.g 2**4 - 1 = 15('1111'). 
# So 'and'ing with 15 will extract 4-bits from LSB.
np.bitwise_or(np.left_shift(np.bitwise_and(data, 2**positions - 1),8 - positions), np.right_shift(data, positions))

I hope the comments in the code are explanatory enough. Works for individual unit8 values as well as numpy arrays. Never measured speed to compare with original answer, but I think it should be similar or better, as no explicit memory allocation/ deallocation or extra operations are being done.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.