【问题标题】:How to find the next lower integer (with the same number of 1s)如何找到下一个较小的整数(具有相同数量的 1)
【发布时间】:2013-10-18 23:47:19
【问题描述】:

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

【问题讨论】:

  • 做你自己的功课兄弟
  • 这不是硬件。只是有趣的东西,看看是否有人能想出一个简短的酷答案。

标签: java computer-science


【解决方案1】:

你可以这样做。

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);
}

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

【讨论】:

  • 这个算法不起作用。尝试使用 25 并返回相同的数字。你没有试过运行它吗?
  • @FredtonDoan 好的,我相信你可以修复一个简单的错误。我建议你要求 StackOverflow 退款。
  • 我不认为这很简单或让你的代码工作。对于大数字,它需要更多位来翻转,这就是实际问题出现的地方。感谢您的尝试:-)
  • @FredtonDoan 好的,给我一个它不起作用的例子。为什么翻转更多位很重要?这可以翻转所有位,因为它会在需要时尝试每个正值。
  • @FredtonDoan 他还说它工作得很好,但他的可能更快。
【解决方案2】:

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

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

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

Peter 算法的稍微快一点的版本应该如下,它对整数执行我建议您对字符串进行的操作:

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%。我认为这不足以证明增加的代码复杂性是合理的。

【讨论】:

  • 这基本上是我的想法。找到最右边的 10 个并翻转它。
【解决方案3】:

我喜欢这样的二进制任务,所以要找到下一个较低的数字,你应该找到最右边的 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;
        }
    }

【讨论】:

  • 你在我还在数位的时候回答了 :) 很棒的方法!
  • 它似乎只能用 1 位翻转。您是否尝试过需要 2 次翻转的 25 号。更大的数字将需要更多的翻转。那是困难的部分。一次翻转非常简单。
  • @FredtonDoan for 25 we found 21,检查while循环
  • 对于 25,它应该返回 22,这是下一个较低的 :-)
  • 是的,我发现了相同的 :( 如果你保留第 0 位(因为这个应该总是改变)并且只向左移动 next 位怎么办?(编辑:不。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-30
  • 1970-01-01
  • 1970-01-01
  • 2020-08-22
  • 2022-01-24
  • 2016-02-15
相关资源
最近更新 更多