【问题标题】:Swapping bits at a given point between two bytes在两个字节之间的给定点交换位
【发布时间】:2011-04-03 12:06:18
【问题描述】:

假设我有这两个数字:

x = 0xB7
y = 0xD9

它们的二进制表示是:

x = 1011 0111
y = 1101 1001

现在我想在给定点交叉 (GA),比如从位置 4 开始。

预期的结果应该是:

x = 1011 1001
y = 1101 0111

按位,我怎样才能做到这一点?

【问题讨论】:

    标签: bit-manipulation genetic-algorithm


    【解决方案1】:

    我只会使用位运算符:

    t = (x & 0x0f)
    x = (x & 0xf0) | (y & 0x0f)
    y = (y & 0xf0) | t
    

    这适用于特定情况。为了使其更具适应性,我将它放在一个函数中,例如(伪代码,&|! 按位表示“与”、“或”和“非”分别):

    def swapBits (x, y, s, e):
        lookup = [255,127,63,31,15,7,3,1]
        mask = lookup[s] & !lookup[e]
        t = x & mask
        x = (x & !mask) | (y & mask)
        y = (y & !mask) | t
        return (x,y)
    

    查找值允许您指定要交换的位。让我们将值 xxxxxxxx 用于 xyyyyyyyy 用于 y 以及 2 的起始位 s 和 6 的结束位 e(在这种情况下,位编号从左侧的零开始):

    x        y        s e t        mask     !mask    execute
    -------- -------- - - -------- -------- -------- -------
    xxxxxxxx yyyyyyyy 2 6                   starting point
                                  00111111  mask = lookup[2](00111111)
                                  00111100       & !lookup[6](11111100)
                          00xxxx00          t = x & mask
    xx0000xx                                x = x & !mask(11000011)
    xxyyyyxx                                  | y & mask(00111100)
             yy0000yy                       y = y & !mask(11000011)
             yyxxxxyy                         | t(00xxxx00)
    

    【讨论】:

    • LUT 比(1<<s) - (1<<e) 更好并不明显。顺便说一句,您可以仅用 3 个 XOR + 1 AND 交换位:请参阅我的答案。
    【解决方案2】:

    如果两个值中的位位置相同,则两者都不需要更改。如果相反,它们都需要反转。

    XOR 与 1 翻转一点;与 0 的 XOR 是空操作。

    所以我们想要的是一个值,在输入之间存在位差的任何地方都有一个1,而在其他任何地方都有一个 0。这正是a XOR b 所做的。

    简单地屏蔽这个位差,只保留我们想要交换的位的差异,我们在 3 个 XOR + 1 AND 中进行了位交换。

    你的面具是(1UL << position) -1。小于 2 的幂的 1 具有低于该集合的所有位。或者更一般地,为您的位范围设置高低位置:(1UL << highpos) - (1UL << lowpos)。查找表是否比 bit-set / sub 更快取决于编译器和硬件。 (请参阅@PaxDiablo 对 LUT 建议的回答)。

    // Portable C:
    
    //static inline
    void swapBits_char(unsigned char *A, unsigned char *B)
    {
        const unsigned highpos = 4, lowpos=0;  // function args if you like
        const unsigned char mask = (1UL << highpos) - (1UL << lowpos);
    
        unsigned char tmpA = *A, tmpB = *B; // read into locals in case A==B
    
        unsigned char bitdiff = tmpA ^ tmpB;
        bitdiff &= mask;               // clear all but the selected bits
        *A = tmpA ^ bitdiff;           // flip bits that differed
        *B = tmpB ^ bitdiff;
    }
    
    //static inline
    void swapBit_uint(unsigned *A, unsigned *B, unsigned mask)
    {
        unsigned tmpA = *A, tmpB = *B;
    
        unsigned bitdiff = tmpA ^ tmpB;
        bitdiff &= mask;               // clear all but the selected bits
        *A = tmpA ^ bitdiff;
        *B = tmpB ^ bitdiff;
    }
    

    (Godbolt compiler explorer with gcc for x86-64 and ARM)

    不是异或交换。它确实使用临时存储。正如@chux's answer 在一个近乎重复的问题上所展示的那样,屏蔽的异或交换需要 3 次 AND 运算以及 3 次 XOR。 (并且通过需要临时寄存器或其他存储空间来存储 &amp; 结果,从而破坏了 XOR 交换的唯一好处。)此答案是我对其他问题的答案的修改副本。

    此版本只需要 1 个 AND。此外,最后两个 XOR 彼此独立,因此从输入到两个输出的总延迟仅为 3 次操作。 (通常为 3 个周期)。


    有关 x86 asm 示例,请参阅此代码高尔夫 Exchange capitalization of two strings in 14 bytes of x86-64 machine code(带有注释的 asm 源)

    【解决方案3】:

    Swapping individual bits with XOR

    unsigned int i, j; // positions of bit sequences to swap
    unsigned int n;    // number of consecutive bits in each sequence
    unsigned int b;    // bits to swap reside in b
    unsigned int r;    // bit-swapped result goes here
    
    unsigned int x = ((b >> i) ^ (b >> j)) & ((1U << n) - 1); // XOR temporary
    r = b ^ ((x << i) | (x << j));
    

    【讨论】:

    • 在一个unsigned int 中交换两个(不重叠的)位域。只有一个输入 (b) 和一个输出 (r)。对于两个值之间对应位的屏蔽异或交换,这不是一个有用的起点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 2014-11-18
    • 2014-08-12
    • 1970-01-01
    • 1970-01-01
    • 2018-02-14
    • 1970-01-01
    相关资源
    最近更新 更多