给定一个整数,交换其中的两个比特位

3
我看到了一些解决方案,但它们看起来很复杂。
在n、m位置交换两个位的最有效方法是什么?
int swapBits(int num, int nPostion, int mPosition);

2
请查看此链接:链接1 - Vaibhav
为什么交换两个位的函数需要int num参数? - Support Ukraine
取一个整数,交换其中的两个位,并返回交换后的新数字。 - I.Mac
6个回答

8

在给定的整数n中,我们想要交换位置p1和p2的比特位: 算法:如果两个比特位相同,则返回相同的值,否则使用异或操作切换两个比特位

unsigned int swapBits(unsigned int n, unsigned int p1, unsigned int p2)
{
  return (((n >> p1) & 1) == ((n >> p2) & 1) ? n : ((n ^ (1 << p2)) ^ (1 << p1)));
}

(1 << p2) should be (1U << p2) to avoid undefined behavior if p2 is one less than the width of unsigned int. Same remark for (1 << p1) - chqrlie

3

我不确定这是最有效的方法,但我认为这是一个相当简单的解决方案:

int bitValue(int num, int nPosition)
{
    return ( num >> nPosition ) % 2;
}

int swapBits(int num, int nPosition, int mPosition)
{
    int nVal = bitValue(num, nPosition);
    int mVal = bitValue(num, mPosition);

    if (nVal != mVal)
    {
        if (1 == nVal)
        {
           num -= 1<<nPosition;
           num += 1<<mPosition;
        }
        else
        {
            num += 1<<nPosition;
            num -= 1<<mPosition;
        }
    }

    return num;
}

同样的解决方案,但更高效(但不太易读)的方式:

int swapBits2(int num, int nPosition, int mPosition)
{
    int nVal = ( num >> nPosition ) % 2;
    int mVal = ( num >> mPosition ) % 2;

    if (nVal != mVal)
    {
        num += (-1)*(2*mVal-1)*(1<<mPosition) + (-1)*(2*nVal-1)*(1<<nPosition);
    }

    return num;
}

并且最后:
int swapBits3(int num, int nPosition, int mPosition)
{
    int k = ((num >> nPosition) & 1) - (num >> mPosition) & 1;

    return num + k*(1<<mPosition) - k*(1<<nPosition);
}

1

Parth Bera的回答中包含一个分支,但异或的想法是正确的。

假设p的位是????A????B???。为了将A变成B,将B变成A,我们需要用(A^B)进行异或运算。为方便起见,令X=A^B

????A????B???
0000X0000X000 ^
=============
????B????A???

我们如何生成 0000X0000X000
????A????B???    >> (nPostion-mPostion)
?????????A???    ^
?????????X???    & (1<<mPosition)
000000000X000    << (nPostion-mPostion)
0000X00000000    +
0000X0000X000    ^
????A????B???    ==
????B????A???

0

您可以使用以下宏来避免临时变量或堆栈分配,并且它适用于任何数值类型:

#define SWAP_BITS(v,b1,b2) \
    (((v)>>(b1)&1)==((v)>>(b2)&1)?(v):(v^(1ULL<<(b1))^(1ULL<<(b2))))

1
为什么要使用一个评估其参数多次的宏,而不是使用内联函数呢? - chqrlie
此外,如果b1b2将最高有效位作为移位,则将类型intlonglong long的有符号类型转换为目标类型undefined behavior,您的代码将无法正常工作。 - chqrlie
是的,我在我的代码中更新了1ULL,现在也更新了我的答案。关于多次评估:是的,可能会发生这种情况,但编译器不应该优化掉它吗,至少如果它知道没有副作用的话?如果您将函数调用作为表达式传递,则宏可能不是最佳选择。但是,如果您想要将其用于多个类型,则需要多次定义函数,不是吗? - hurikhan77
编译器无法将多个评估优化掉,宏会被逐字替换,编译器必须假定多个评估是有意的。宏确实很有用,可以处理多种类型,但新的“_Generic”结构允许宏根据参数类型扩展到适当的函数调用,例如:#define swap_bits(v,b1,b2) _Generic(+(v), unsigned int: swap_bits_uint, unsigned long: swap_bits_ulong, unsigned long long: swap_bits_ullong)(v, b1, b2) - chqrlie

0

在Shay Gold的解决方案基础上,这里提供了一个修复了一些错误的版本:

unsigned int swapBits(unsigned int num, int nPosition, int mPosition) {
    unsigned int k = ((num >> nPosition) & 1) - ((num >> mPosition) & 1);
    return num + k * ((1U << mPosition) - (1U << nPosition));
}

难道不应该是反过来的吗:int swapBits(int num...unsigned int ...position?此外,我不明白为什么移位负数应该是未定义的(尽管您的链接表明了这一点),如果我们将位移入符号位或从符号位移出位,那么数字就会交换符号。它并不特别未定义,但可能是意料之外的... - hurikhan77
@hurikhan77:C标准明确规定移位负值的行为是未定义的。在其他语言中,如Java和JavaScript,使用>>向右移位负值被定义为执行符号复制操作,一些C实现也会这样做,但C标准并不强制要求这种行为,也不是你所描述的那种行为。 - chqrlie

0
unsigned int swapbits(unsigned int num, unsigned int pos1, unsigned int pos2) {
    unsigned int bits_of_interest_mask = (1 << pos1) | (1 << pos2);
    unsigned int bits_of_interest = num & bits_of_interest_mask;
    unsigned int null_factor = ((bits_of_interest != 0) & (bits_of_interest != bits_of_interest_mask));
    unsigned int xor_mask = null_factor * bits_of_interest_mask;
    return num ^ xor_mask;
}

(编译器会删除布尔乘法:https://godbolt.org/z/a4z3jnh7c, https://godbolt.org/z/aM3TK7bq1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接