我想在位表示中生成所有可能的组合(不重复)。我不能使用像boost或stl :: next_combination这样的库 - 它必须是我自己的代码(计算时间非常重要)。
这是我的代码(从StackOverflow用户修改):
int combination = (1 << k) - 1;
int new_combination = 0;
int change = 0;
while (true)
{
// return next combination
cout << combination << endl;
// find first index to update
int indexToUpdate = k;
while (indexToUpdate > 0 && GetBitPositionByNr(combination, indexToUpdate)>= n - k + indexToUpdate)
indexToUpdate--;
if (indexToUpdate == 1) change = 1; // move all bites to the left by one position
if (indexToUpdate <= 0) break; // done
// update combination indices
new_combination = 0;
for (int combIndex = GetBitPositionByNr(combination, indexToUpdate) - 1; indexToUpdate <= k; indexToUpdate++, combIndex++)
{
if(change)
{
new_combination |= (1 << (combIndex + 1));
}
else
{
combination = combination & (~(1 << combIndex));
combination |= (1 << (combIndex + 1));
}
}
if(change) combination = new_combination;
change = 0;
}
其中n
- 所有元素,k
- 组合元素的数量。
GetBitPositionByNr
- 返回第k位的位置。
GetBitPositionByNr(13,2) = 3
因为13是1101而第二位是第三位。
它为n=4, k=2
提供了正确的输出:
0011 (3 - decimal representation - printed value)
0101 (5)
1001 (9)
0110 (6)
1010 (10)
1100 (12)
它还为k=1
和k=4
提供了正确的输出,但却为k=3
提供了错误的输出:
0111 (7)
1011 (11)
1011 (9) - wrong, should be 13
1110 (14)
我想这个问题是在内在的条件下(第二个),但我不知道如何解决这个问题。
也许有些人知道更好(更快)的算法想要实现吗?它无法使用额外的内存(数组)。
以下是在ideone上运行的代码:IDEONE
答案 0 :(得分:0)
如有疑问,请使用蛮力。唉,通过重复生成所有变体,然后过滤掉不必要的模式:
unsigned bit_count(unsigned n)
{
unsigned i = 0;
while (n) {
i += n & 1;
n >>= 1;
}
return i;
}
int main()
{
std::vector<unsigned> combs;
const unsigned N = 4;
const unsigned K = 3;
for (int i = 0; i < (1 << N); i++) {
if (bit_count(i) == K) {
combs.push_back(i);
}
}
// and print 'combs' here
}
编辑:其他人已经指出了一个没有过滤和暴力的解决方案,但我仍然会给你一些关于这个算法的提示:
大多数编译器提供某种内在人口统计功能。我知道GCC和Clang有__builtin_popcount()
。使用这个内在函数,我能够使代码的速度加倍。
由于您似乎正在使用GPU,因此您可以并行化代码。我使用C ++ 11的标准线程工具完成了它,并且我已经设法计算所有32在我的8核Intel机器上,在7.1秒内对任意选择的popcounts 1,16和19进行重复比赛。
这是我写的最终代码:
#include <vector>
#include <cstdio>
#include <thread>
#include <utility>
#include <future>
unsigned popcount_range(unsigned popcount, unsigned long min, unsigned long max)
{
unsigned n = 0;
for (unsigned long i = min; i < max; i++) {
n += __builtin_popcount(i) == popcount;
}
return n;
}
int main()
{
const unsigned N = 32;
const unsigned K = 16;
const unsigned N_cores = 8;
const unsigned long Max = 1ul << N;
const unsigned long N_per_core = Max / N_cores;
std::vector<std::future<unsigned>> v;
for (unsigned core = 0; core < N_cores; core++) {
unsigned long core_min = N_per_core * core;
unsigned long core_max = core_min + N_per_core;
auto fut = std::async(
std::launch::async,
popcount_range,
K,
core_min,
core_max
);
v.push_back(std::move(fut));
}
unsigned final_count = 0;
for (auto &fut : v) {
final_count += fut.get();
}
printf("%u\n", final_count);
return 0;
}