1

如何找到整数(相同数量的 1)的下一个较低二进制数?例如:如果给定输入数 n = 10 (1010),则函数应返回 9 (1001),或者 n = 14 (1110) 然后返回 13 (1101),或者 n = 22 (10110) 然后返回 21 (10101) , n = 25 (11001) 然后返回 22 (10110)... 等等。

4

3 回答 3

4

你可以这样做。

static int nextLower(int n) {
   int bc = Integer.bitCount(n);
   for (int i = n - 1; i > 0; i--)
     if (Integer.bitCount(i) == bc)
        return i;
   throw new RuntimeException(n+" is the lowest with a bit count of "+bc);
}

当然,如果这是家庭作业,你将很难说服你写这篇文章的人;)

于 2013-10-19T00:05:49.427 回答
2

为了清楚起见,在这个答案中,我将使用术语“基数”来表示数字二进制表示中 1 的数量。

一种(显而易见的)方法是运行向下循环,并寻找与您的输入具有相同基数的第一个数字(就像 Peter Lawrey 建议的那样)。

我不认为这是低效的,因为我猜输出数字总是非常接近输入。更准确地说,您所要做的就是找到最右边的“10”位序列,并将其更改为“01”。然后将右侧部分替换为左侧全为 1 的数字,在不破坏后置条件的情况下尽可能多地替换。这给我们带来了另一个解决方案,它包括将数字转换为二进制字符串(就像 user2573153 向您展示的那样),执行替换(可能使用正则表达式),然后转换回 int。

下面是彼得算法的一个稍快的版本,它对整数执行我建议你对字符串的操作:

static int nextLower(int n) {
    int fixPart = 0;
    int shiftCount = 0;

    while ((n & 3) != 2) {
        if (n == 0) {
            throw new IllegalArgumentException(
                    fixPart + " is the lowest number with its cardinality");
        }

        fixPart |= (n & 1) << shiftCount;
        shiftCount += 1;
        n /= 2;
    }

    int fixZeros = shiftCount - Integer.bitCount(fixPart);
    return ((n ^ 3) << shiftCount) | (((1 << shiftCount) - 1) & ~((1 << fixZeros) - 1));
}

这是 O(log n) 而不是 O(n),但由于其复杂性,它肯定更难理解,并且实际上也可能更慢。无论如何,如果您尝试使用一些非常困难的数字,您只会注意到差异。

编辑

我尝试了一个小基准测试,发现当连续应用于从 2 到 100,000,000 的所有数字时,此代码比 Peter Lawrey 的代码快 67% 。我认为这不足以证明增加的代码复杂性是合理的。

于 2013-10-19T00:40:45.953 回答
1

我喜欢这样的二进制任务,所以要找到下一个较低的数字,你应该找到最右边的 1,然后是 0 并交换它们。更新:您需要“重新排序”数字的其余部分,左侧为 1,右侧为 0

10    1010 ->
 9    1001
14    1110 ->
13    1101
25   11001 ->
22   10110

这是示例代码:

    int originalValue = 25;
    int maskToCheck = 2; // in binary 10b
    int clearingMask = 1;
    int settingMask = 0;
    int zeroCount = 0;
    while (maskToCheck > 0)
    {
        if ( (originalValue&(maskToCheck|(maskToCheck>>1))) == maskToCheck ) // we found such
        {
            int newValue = originalValue&(~maskToCheck); // set 1 with 0
            newValue = newValue&(~clearingMask)|(settingMask<<zeroCount); // clear all the rest bits, and set most valuable ones
            newValue = newValue|(maskToCheck>>1); // set 0 with 1
            System.out.println("for " + originalValue + " we found " + newValue);
            break;
        }
        else
        {
            if ( (originalValue&(maskToCheck>>1)) > 0) // we have 1 bit in cleared part
                settingMask = (settingMask<<1) | 1;
            else
                zeroCount++;
            maskToCheck = maskToCheck<<1; // try next left bits
            clearingMask = (clearingMask<<1)|1;
        }
    }
于 2013-10-19T00:10:26.180 回答