计算具有相同汉明重量的二进制数的所有排列的最快算法是什么?

时间:2015-01-03 14:04:38

标签: algorithm combinations hammingweight

我想要一个算法来计算具有给定汉明重量的固定大小二进制数的所有排列。例如,如果汉明重量为2且二进制大小为4,则有这些输出:

0011
0110
0101
1100
1010
1001

在此示例C(n,r)中,此类组合的数量计算为C(4,2),即6。

请注意,您只需将数字从0增加到2 ^ n即可解决此问题,并查看计数是否正常。但是,它不是一个快速的解决方案。 我正在考虑使用C ++中的bitset类解决问题,我需要增加N。

我想补充一点,这个问题有一个明显的递归算法。由于堆栈溢出,这不是一个好的答案。我从Gosper的黑客那里得到了一个很好的答案。虽然,我需要扩大输入,并可能稍后使用MPI实现,我需要一个可扩展的库。 unsigned int不够大,我宁愿像bitset一样可扩展且快速的库。此处的解决方案不适用,而bitset库中没有添加。任何其他解决方案?

2 个答案:

答案 0 :(得分:5)

您可以使用Gosper's Hack实现“按字典顺序排列的下一位置换”:

unsigned int v; // current permutation of bits 
unsigned int w; // next permutation of bits

unsigned int t = v | (v - 1); // t gets v's least significant 0 bits set to 1
// Next set to 1 the most significant bit to change, 
// set to 0 the least significant ones, and add the necessary 1 bits.
w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));

或者,如果您没有ctz(MSVC上为_BitScanForward),

unsigned int t = (v | (v - 1)) + 1;  
w = t | ((((t & -t) / (v & -v)) >> 1) - 1);

答案 1 :(得分:2)

您可以通过以下方式生成它们:

  1. 最初,在开头创建一个n - r个零的向量,在结尾处创建r个向量(0011,n = 4且r = 2)。

  2. 然后,重复以下步骤:

    1. 找到最右边的一个,使零位于其左侧。 如果没有这样的话,我们就完成了。
    2. 将其向左移动(通过一个位置,即将其与零交换)。
    3. 将位于右侧的所有位置移动到向量的最末端 例如,如果我们有0110,我们首先移动最右边的一个可以向左移动并得到1010,然后我们将所有的一个移到它的右边,直到向量的末尾。得到1001
  3. 此解决方案的时间复杂度为O(C(n, r) * n)。该解决方案的另一个特点是:它按字典顺序生成元素。