范围 [a,b] 中的最小数字,二进制表示中最大数量为“1”

Smallest number in a range [a,b] with maximum number of '1' in binary representation

给定一个范围 [a,b](包括两者),我需要在二进制表示中找到具有最大“1”数量的最小数字。我目前的方法是找到从 a 到 b 的所有数字中设置的位数,并跟踪最大值。 然而这很慢,有没有更快的方法?

让我们找出a和b中不同的最高有效位。它将在 a 中为 0,在 b 中为 1。如果我们将所有其他位放在 1 的右边 - 结果数字仍将在范围 [a; b].并且它将是表示中个数最多的单个数。

编辑。该算法的结果总是 returns 将 n-1 位设置为 1 的数字,其中 n 是可以更改的位数。正如评论中指出的那样 - 如果 b 中的所有 n 位都设置为 1,则会出现错误。这是固定的代码片段:

int maximizeBits(int a, int b) {
    if (a == b) {
        return a;
    }
    int m = a ^ b, pow2 = 1; // MSB of m=a^b is bit that we need to find
    while (m > pow2) { // Set other bits to 0
        if ((m & pow2) != 0) {
            m ^= pow2;
        }
        pow2 <<= 1;
    }

    int res = a | (m - 1); // Now m is in form of 2^n and m - 1 would be mask of n-1 bits
    if ((res | b) <= b) { // Fix of problem if all n bits in b are set to 1
        res = b;
    }
    return res;
}

您可以用 "parallel suffix OR" 替换 Jarlax 答案中的循环,像这样

uint32_t m = (a ^ b) >> 1;
m |= m >> 1;
m |= m >> 2;
m |= m >> 4;
m |= m >> 8;
m |= m >> 16;
uint32_t res = a | m;
if ((res | b) <= b)
    res = b;
return res;

它概括为不同大小的整数,一般使用 ceil(log(k)) 步骤。初始测试 a == b 不是必需的,a ^ b 将为零,因此 m 为零,因此没有任何有趣的事情发生。


或者,这是一种完全不同的方法:不断将最低的 0 更改为 1,直到它不再可能为止。

unsigned x = a;
while (x < b) {
    unsigned newx = (x + 1) | x; // set lowest 0
    if (newx <= b)
        x = newx;
    else
        break;
}
return x;