How would you transpose a binary matrix?

前端 未结 7 982
-上瘾入骨i
-上瘾入骨i 2020-12-16 02:13

I have binary matrices in C++ that I repesent with a vector of 8-bit values.

For example, the following matrix:

1 0 1 0 1 0 1
0 1 1 0 0 1 1
0 0 0 1 1         


        
7条回答
  •  轻奢々
    轻奢々 (楼主)
    2020-12-16 02:55

    My suggestion would be to use a lookup table to speed up the processing.

    Another thing to note is with the current definition of your matrix the maximum size will be 8x8 bits. This fits into a uint64_t so we can use this to our advantage especially when using a 64-bit platform.

    I have worked out a simple example using a lookup table which you can find below and run using: http://www.tutorialspoint.com/compile_cpp11_online.php online compiler.

    Example code

    #include 
    #include 
    #include 
    #include 
    
    using std::cout;
    using std::endl;
    using std::bitset;
    
    /* Static lookup table */
    static uint64_t lut[256];
    
    /* Helper function to print array */
    template
    void print_arr(const uint8_t (&arr)[N]){
        for(int i=0; i < N; ++i){
            cout << bitset<8>(arr[i]) << endl;
        }
    }
    
    /* Transpose function */
    
    template
    void transpose_bitmatrix(const uint8_t (&matrix)[N], uint8_t (&transposed)[8]){
        assert(N <= 8);
    
        uint64_t value = 0;
        for(int i=0; i < N; ++i){
            value = (value << 1) + lut[matrix[i]];
        }
    
        /* Ensure safe copy to prevent misalignment issues */
        /* Can be removed if input array can be treated as uint64_t directly */
        for(int i=0; i < 8; ++i){
            transposed[i] = (value >> (i * 8)) & 0xFF;
        }
    }
    
    /* Calculate lookup table */
    void calculate_lut(void){
        /* For all byte values */
        for(uint64_t i = 0; i < 256; ++i){
            auto b = std::bitset<8>(i);
            auto v = std::bitset<64>(0);
    
            /* For all bits in current byte */
            for(int bit=0; bit < 8; ++bit){
                if(b.test(bit)){
                    v.set((7 - bit) * 8);
                }
            }
    
            lut[i] = v.to_ullong();
        }
    }
    
    int main()
    {
        calculate_lut();
    
        const uint8_t matrix[] = {
            0b01010101,
            0b00110011,
            0b00001111,
        };
    
        uint8_t transposed[8];
    
        transpose_bitmatrix(matrix, transposed);
        print_arr(transposed);
    
       return 0;
    }
    

    How it works

    your 3x8 matrix will be transposed to a 8x3 matrix, represented in an 8x8 array. The issue is that you want to convert bits, your "horizontal" representation to a vertical one, divided over several bytes.

    As I mentioned above, we can take advantage of the fact that the output (8x8) will always fit into a uint64_t. We will use this to our advantage because now we can use an uint64_t to write the 8 byte array, but we can also use it for to add, xor, etc. because we can perform basic arithmetic operations on a 64 bit integer.

    Each entry in your 3x8 matrix (input) is 8 bits wide, to optimize processing we first generate 256 entry lookup table (for each byte value). The entry itself is a uint64_t and will contain a rotated version of the bits.

    example:

    byte = 0b01001111 = 0x4F
    lut[0x4F] = 0x0001000001010101 = (uint8_t[]){ 0, 1, 0, 0, 1, 1, 1, 1 }

    Now for the calculation:

    For the calculations we use the uint64_t but keep in mind that under water it will represent a uint8_t[8] array. We simple shift the current value (start with 0), look up our first byte and add it to the current value.

    The 'magic' here is that each byte of the uint64_t in the lookup table will either be 1 or 0 so it will only set the least significant bit (of each byte). Shifting the uint64_t will shift each byte, as long as we make sure we do not do this more than 8 times! we can do operations on each byte individually.

    Issues

    As someone noted in the comments: Translate(Translate(M)) != M so if you need this you need some additional work.

    Perfomance can be improved by directly mapping uint64_t's instead of uint8_t[8] arrays since it omits a "safe-copy" to prevent alignment issues.

提交回复
热议问题