【问题标题】:Power set generated by bits位产生的功率集
【发布时间】:2013-10-18 10:28:48
【问题描述】:

我有这段代码可以生成大小为 4 的数组的幂集(数字只是示例,要编写的组合更少...)。

#define ARRAY_SIZE 4


unsigned int i, j, bits, i_max = 1U << ARRAY_SIZE;
int array[ARRAY_SIZE];

for (i = 0; i < i_max ; ++i) {
    for (bits = i, j = 0; bits; bits >>= 1, ++j) {
        if (bits & 1)
            printf("%d", array[j]);
    }
}

输出:

{}
{1}
{2}
{1, 2}
{3}
{1, 3}
{2, 3}
{1, 2, 3}
{4}
{1, 4}
{2, 4}
{1, 2, 4}
{3, 4}
{1, 3, 4}
{2, 3, 4}
{1, 2, 3, 4}

我需要这样的输出:

{1}
{2}
{3}
{4}
{1, 2}
{1, 3}
{1, 4}
{2, 3}
{2, 4}
{3, 4}
{1, 2, 3}
{1, 2, 4}
{1, 3, 4}
{2, 3, 4}
{1, 2, 3, 4}

所以必须这样订购。我不能在算法结束后进行排序,我必须在每次迭代中使用每个组合,所以它必须生成已经排序的组合。有人可以帮我吗?我想我正在考虑一切......

编辑:最终输出应该没有空集,但这不是优先级。

【问题讨论】:

  • 您的意思是在第二种情况下省略空集吗?从数学上讲,它是幂集的完全有效成员。
  • 我是认真的,只是忘了提,抱歉。谢谢。
  • 实际上,你想要的并不是按字典顺序排列的。它按大小排序,然后按字典顺序排列在大小相等的组中(字典中香蕉排在猫之前)。
  • 再次感谢!已编辑;)
  • 这样一个存在的问题当然必须用 42 行代码来回答(见我的回答);-)

标签: c++ algorithm subset powerset bit-manipulation


【解决方案1】:

这是一个通过小玩意深入到金属的版本。它使用著名的Hackers' Delight snoob() 函数的修改版本,该函数生成具有Same Number Of One Bits 的下一个更大的子集。请参阅Chess Programming Wiki(许多此类比特旋转例程的来源)。

#include <cstdint>
#include <iostream>

using U64 = uint64_t;

// print bit indices of x
void setPrint(U64 x)
{
    std::cout << "{ ";
    for(auto i = 1; x; x >>= 1, ++i)
        if (x & 1) std::cout << i << ", ";
    std::cout << "}\n";
}

// get next greater subset of set with 
// Same Number Of One Bits
U64 snoob (U64 sub, U64 set) {
   U64 tmp = sub-1;
   U64 rip = set & (tmp + (sub & (0-sub)) - set);
   for(sub = (tmp & sub) ^ rip; sub &= sub-1; rip ^= tmp, set ^= tmp)
       tmp = set & (0-set);
   return rip;
}

void generatePowerSet(unsigned n)
{
    auto set = (1ULL << n) - 1;                 // n bits
    for (unsigned i = 0; i < n+1; ++i) {
        auto sub = (1ULL << i) - 1;             // i bits
        U64 x = sub; U64 y; 
        do {            
            setPrint(x);
            y = snoob(x, set);                  // get next subset
            x = y;
        } while ((y > sub));
    }
}

int main()
{
    generatePowerSet(4);    
}

Live Example 按字典顺序输出(奖励功能)

{ }
{ 1, }
{ 2, }
{ 3, }
{ 4, }
{ 1, 2, }
{ 1, 3, }
{ 2, 3, }
{ 1, 4, }
{ 2, 4, }
{ 3, 4, }
{ 1, 2, 3, }
{ 1, 2, 4, }
{ 1, 3, 4, }
{ 2, 3, 4, }
{ 1, 2, 3, 4, }

【讨论】:

  • 嗯,这是一个很好的解决方案:)
  • tnx,很高兴能帮上忙!
【解决方案2】:

编写一个函数来生成所有固定大小的项目。请注意,第一个这样的项目是 1,..,k,而最后一个是 (n-k+1),..,n。这很简单,您基本上必须重新实现基本计数:增加最后一个“数字”,直到达到 n。然后你重置为 1 并以同样的方式继续到它的左边。

然后让 k 从 1 (0) 运行到 n。

这是内部算法的一种建议。不过,它相当丑陋。

void out(std::vector<int> const& item) {
  char del = '{';
  for (auto it = item.begin(); it != item.end(); ++it) {
    std::cout << del << *it;
    del = ',';
  }
  std::cout << "}\n";
}

bool ascending(std::vector<int> const& item) {
  int last = 0;
  for (auto it = item.begin(); it != item.end(); ++it) {
    if (*it <= last)
      return false;
    last = *it;
  }
  return true;
}

void allk(int k, int n) {
  std::vector<int> item;
  item.reserve(k+1);
  for (int i = 1; i <= k; i++)
    item.push_back(i);
  bool valid = true;
  while (valid) {
    out(item);
    do {
      valid = false;
      for (auto it = item.rbegin(); it != item.rend(); ++it) {
        if (*it == n) {
          *it = 1;
        } else {
          ++*it;
          valid = true;
          break;
        }
      }
    } while (valid && !ascending(item));
  }
}

【讨论】:

  • 如果原始集是{1, ..., n},则生成值。否则,如果原始集合具有n 元素,则会在该集合中生成基于1 的索引。 (如果您使用的是基于0 的容器,请记住-1;很可能在C++ 中)。
  • @BoBTFish:重复并不完全是微不足道的。我正在考虑一个简单的规则来消除这些。
【解决方案3】:

使用缓冲区数组,然后使用 qsort 对其进行排序?

我使用 i_max*5 元素数组:4 unsigned int 用于值(0 表示空,1 到 4 否则)和子集长度的最终无符号整数。然后,您只需要使用自定义比较器即可。如果您想要更精简的算法,请查看插入排序:http://en.wikipedia.org/wiki/Insertion_sort

输出:

1,
2,
1,2,
3,
1,3,
2,3,
1,2,3,
4,
1,4,
2,4,
1,2,4,
3,4,
1,3,4,
2,3,4,
1,2,3,4,

Sorting
1,
2,
3,
4,
1,2,
1,3,
1,4,
2,3,
2,4,
3,4,
1,2,3,
1,2,4,
1,3,4,
2,3,4,
1,2,3,4,

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int comp (const void * elem1, const void * elem2) {
    unsigned int *f = (unsigned int*)elem1;
    unsigned int *s = (unsigned int*)elem2;
    unsigned int i;


    //printf("%d,%d,%d,%d,%d\n", f[0],f[1],f[2],f[3],f[4]);
    //printf("%d,%d,%d,%d,%d\n", s[0],s[1],s[2],s[3],s[4]);
    printf("\n");

    // Size comparison     
    if( f[4] > s[4] ) return 1;
    if( f[4] < s[4] ) return -1;

    // Value comparison
    for ( i = 0; i < 4; i++)
    {
        if (f[i] > s[i]) return  1;
        if (f[i] < s[i]) return -1;
    }

    return 0;
}

int main(int argc, char *argv[])
{
    unsigned int i, j, bits, i_max = (1U << 4);
    unsigned int *array;
    unsigned int index;

    array = calloc( 5*i_max, sizeof(unsigned int));

    // Non-sorted array
    for (i = 0; i < i_max ; ++i) {

        // Zero init for array
        memset(array + i*5, 0, 5*sizeof(*array));
        index = 0;

        for (bits = i, j = 0; bits; bits >>= 1, ++j) {

        if (bits & 1)
            {
                // Storage
                array[i*5 + index]   = j+1;

                index = (index + 1) % 4; // avoid buffer overflows
                array[i*5 + 4]   = array[i*5 + 4] + 1 ; // increment subset length

                //printf("%d,", array[i*5 + index - 1]);
            }
        }
        printf("\n");
    }

    // Print original array, without the zeros
    for (i =0; i < i_max; i++)
    {
        for(j = 0; j < 4 ; j++)
        {
            if( array[i*5 + j] )
                printf("%d,", array[i*5 + j]);
        }
        printf("\n");
    }


    printf ("Sorting\n");

    qsort (array, i_max, 5*sizeof(unsigned int), comp);


    // Print the sorted array, without the zeros
    for (i =0; i < i_max; i++)
    {
        for(j = 0; j < 4 ; j++)
        {
            if( array[i*5 + j] )
                printf("%d,", array[i*5 + j]);
        }
        printf("\n");
    }
    free(array);

    return 0;
}

【讨论】:

  • 令人钦佩,但是您缺少空集{}...所以从技术上讲,这不会生成幂集
【解决方案4】:
  • 我从一个简单的递归置换生成器开始。
  • 添加了一个长度参数,当长度达到 0 时,无需进一步排列,只需打印我们目前拥有的字符串。
  • 添加了我们必须继续从中挑选字符的位置。

代码:

(我知道有些人不喜欢原始数组/指针,但与漂亮的容器相比,解决这个问题更容易获得出色的性能)

void powerSet(char *arr, int arrLen, int pos, int startPos, int length)
{
   if (length == 0)
      printf("%.*s\n", pos, arr);
   else
      for (int i = startPos; i < arrLen; i++)
      {
         std::swap(arr[pos], arr[i]);
         powerSet(arr, arrLen, pos+1, i+1, length - 1);
         std::swap(arr[pos], arr[i]);
      }
}

以及调用函数:

void powerSet(char *arr, int arrLen)
{
    for (int i = 1; i <= arrLen; i++)
      powerSet(arr, 4, 0, 0, i);
}

powerSet("1234", 4) 打印:

1
2
3
4
12
13
14
23
24
34
123
124
134
234
1234

Test.

【讨论】:

  • 我首先想到了类似的事情,但是 OP 想要所有 sets,所以 AC = CA.
【解决方案5】:

我使用this 的答案制作了以下代码:

基本上你需要为所有r打印nCr序列

#include <iostream>
#include <vector>

using namespace std;

 //This generates all nCr sequences
void display(const std::vector<int> & v, 
             std::vector<int>& comb, 
             int offset, int k) {
    if (k == 0) {
        std::cout<<"{";
        for(auto &x:comb)
            std::cout<<x<<",";
         std::cout<<"\b}"<<std::endl;
    return;
    }
    for (int i = offset; i <= v.size() - k; ++i) {
        comb.push_back(v[i]);
        display(v,comb,i+1, k-1);
        comb.pop_back();
    }
}

int main() {
    int n = 4;

     std::vector<int> v,comb;
    for (int i = 0; i < n; ++i) 
     v.push_back(i+1); 

    for (int i = 1; i <= n; ++i) 
        display(v,comb,0, i);

    return 0;
}

here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-28
    • 1970-01-01
    • 1970-01-01
    • 2021-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多