【问题标题】:Computing Combinations on basis of number of bits set根据设置的位数计算组合
【发布时间】:2013-03-03 02:59:16
【问题描述】:

我需要在 0

0         // numbers with 0 bits set

1 2 4     // numbers with 1 bits set

3 5 6     // numbers with 2 bits set

7         // numbers with 3 bits set

我需要知道如何生成数字,以便按照位集的递增/递减顺序对它们进行排序?

【问题讨论】:

标签: c++ c algorithm


【解决方案1】:

实施常规算法来生成组合,但还要保存一个额外的数组,用于存储按照 1 位集排序的数字。然后对于生成的每个组合,将数字替换为对应位置的数字减去我所描述的排序数组中的一。

【讨论】:

  • 如何高效地得到按1位设置排序的数字?
  • 事实上,您不需要高效地执行此操作 - 您只需执行一次,然后生成所有组合。事实是,即使是你能想出的最糟糕的实现也几乎不会产生任何影响。
【解决方案2】:

quant_dev here 很好地涵盖了迭代一些项目的所有组合。

【讨论】:

    【解决方案3】:

    这是一个简单的方法函数,用于计算数字表示中设置的位数:

    // Counts how many bits are set in the representation of the input number n
    int numOfBitsSet(int n)
    {
        int cnt = 0;
        while (n != 0)
        {
            cnt += (n & 1);
            n = n >> 1;
        }
    
        return cnt;
    }
    

    下面是如何在 (C++11) 程序中使用它来做你想做的事情:

    #include <algorithm>
    #include <vector>
    #include <iostream>
    #include <iterator>
    
    using namespace std;
    
    int main()
    {
        // For instance...
        int n = 3;
    
        // Fill up a vector of 2^n entries (0 .. 2^(n - 1))
        vector<int> v(1 << n);
        iota(begin(v), end(v), 0);
    
        // For each number of bits...
        for (size_t i = 0; i <= n; i++)
        {
            cout << "Numbers with " << i << " bits set: ";
    
            // Find the first number with i bits set...
            auto it = find_if(begin(v), end(v), [i] (int x) { 
                return (numOfBitsSet(x) == i); 
                });
    
            while (it != end(v))
            {
                cout << *it << " ";
    
                // Find the next number with i bits set...
                it = find_if(next(it), end(v), [i] (int x) { 
                    return (numOfBitsSet(x) == i); 
                    });
            }
    
            cout << endl;
        }
    }
    

    如果 C++11 不适合您,您将不得不使用仿函数而不是 lambda,并将 std::iota 替换为手动循环:

    #include <algorithm>
    #include <vector>
    #include <iostream>
    #include <iterator>
    
    using namespace std;
    
    struct bit_count_filter
    {
        bit_count_filter(int i) : _i(i) { }
        bool operator () (int x) const { return numOfBitsSet(x) == _i; }
        int _i;
    };
    
    int main()
    {
        // For instance...
        int n = 3;
    
        // Fill up a vector of 2^n entries (0 .. 2^(n - 1))
        vector<int> v(1 << n);
        for (size_t i = 0; i < v.size(); i++)
        {
            v[i] = i;
        }
    
        // For each number of bits...
        for (size_t i = 0; i <= n; i++)
        {
            cout << "Numbers with " << i << " bits set: ";
    
            // Find the first number with i bits set...
            auto it = find_if(begin(v), end(v), bit_count_filter(i));
            while (it != end(v))
            {
                cout << *it << " ";
    
                // Find the next number with i bits set...
                it = find_if(next(it), end(v), bit_count_filter(i));
            }
    
            cout << endl;
        }
    }
    

    【讨论】:

    • pow(2, n)代替1 &lt;&lt; n
    • 并且itoa 应该使用2 的基数,而不是0。此外,itoa 不接受两个迭代器,但第一个参数是数字,第二个是目标 C 字符串。我不明白如何将它与矢量一起使用。你的意思是用零填充向量吗?
    • @leemes:那不是itoa,那是iota
    • @leemes:不用抱歉,您的评论帮助我发现std::iota 不在 C++98 中;-)
    【解决方案4】:

    你可以递归地做:

    void setnbits(unsigned int cur, int n, int toset, int max)
    {
        if(toset == 0)
        {
            printf("%d ", cur >> (n + 32 - max) , n);
            return;
        }
        toset--;
        for(int i = 1 ; i <= n-toset ; i++)
        {
            setnbits((cur >> i) | 0x80000000, n-i, toset , max);
        }
    }
    

    可以这样称呼:

    for(int z = 0 ; z < 4 ; z++)
    {
        printf("%d bits: ", z);
        setnbits(0, 3, z, 3);
        printf("\n");
    }
    

    打印:

    0 bits: 0
    1 bits: 1 2 4
    2 bits: 3 5 6
    3 bits: 7
    

    这些数字不保证按数字顺序排列。

    【讨论】:

      【解决方案5】:

      这很容易。 有两种情况:

      1) 最后 1 位之前有 0 位: 000111001001 -> 000111001010.

      你应该把它移到左边

      2) 有一个 1 位链: 000110111100 -> 000111000111

      然后您应该将最后一位移动到左侧(链之前)最近的 0 位,并将该链的所有其他位移动到右侧。

      您将通过这种方式按升序获得所有需要的数字。

      【讨论】:

      • 我不确定我是否遗漏了什么,但是这个数字链必须从某个地方开始(大概是 0),但是现在没有规则我应该如何处理 0,因为没有设置任何位。显然,接下来是 1。然后我应用第一条规则,直到我有 0b100...0,但是我该怎么办,再次缺少获取下一个数字的规则(在这种情况下为 3 )。
      • 您从 0b00000...0011...111 开始,并且始终具有 r 1 位。如果你得到 0b11...111000.000,这是最后一个数字。您应该做的就是将最后一位向前移动,并将此链中的另一个 1 位向后移动,仍然有 r 1 位。
      猜你喜欢
      • 1970-01-01
      • 2021-12-11
      • 2021-11-01
      • 2012-05-07
      • 1970-01-01
      • 2013-06-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多