从集合中生成大小为k的所有子集

时间:2010-12-29 15:51:24

标签: c++ c algorithm

我想从一组中生成大小为k的所有子集。

例如:-say我有一组6个元素,我必须列出元素基数为3的所有子集。

我尝试寻找解决方案,但这些是代码片段。 自从我编写代码以来,它已经很久了,所以我发现很难理解代码并构建一个可执行程序。

C或C ++中的完整可执行程序将非常有用。 希望使用递归的最佳解决方案。

10 个答案:

答案 0 :(得分:18)

找到下面的工作代码

#include<iostream>
#include<string>
#include<list>

using namespace std;

void print( list<int> l){
    for(list<int>::iterator it=l.begin(); it!=l.end() ; ++it)
            cout << " " << *it;
    cout<<endl;
}

void subset(int arr[], int size, int left, int index, list<int> &l){
    if(left==0){
        print(l);
        return;
    }
    for(int i=index; i<size;i++){
        l.push_back(arr[i]);
        subset(arr,size,left-1,i+1,l);
        l.pop_back();
    }

}     

int main(){
    int array[5]={1,2,3,4,5};
    list<int> lt;   
    subset(array,5,3,0,lt);


    return 0;
}

答案 1 :(得分:15)

使用(1<<nbits)-1初始化位数组,然后使用此算法:

http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation

对于大于最大整数大小的集合,您仍然可以将相同的算法应用于您自己的类型。

答案 2 :(得分:8)

#include <cstdio>
void g(int s[],int p,int k,int t[],int q=0,int r=0)
{
    if(q==k)
    {
        for(int i=0;i<k;i++)
            printf("%d ",t[i]);
        printf("\n");
    }
    else
    {
        for(int i=r;i<p;i++)
        {
            t[q]=s[i];
            g(s,p,k,t,q+1,i+1);
        }
    }
}

main()
{
    int s[]={1,2,3,4,5},t[5];
    g(s,5,3,t);
}

答案 3 :(得分:3)

问题可以使用递归来解决。我们需要考虑以下递归的情况。

  1. 选择当前元素。现在我们递归选择 其余组合中剩余的k-1个元素。(包含)
  2. 未选择当前元素。现在我们递归地从剩下的集合中选择k个元素。(排除)
  3. 以下是演示上述算法的C ++程序。

    #include<iostream>
    #include<cstdio>
    
    using namespace std;    
    
    void KSubset(int *a,int n,int *s,int sindex,int index,int k){
    
        if (index>n)
            return;
    
        if (k==0){
            for(int i=0;i<sindex;i++)
                printf(" %d ",s[i]);
            printf("\n");
            return ;
            }
    
        s[sindex]=a[index];
        KSubset(a,n,s,sindex+1,index+1,k-1);
        KSubset(a,n,s,sindex,index+1,k);
    }
    
    
    int main(){
    
        int a[]={1,2,3,4,5};
        int s[3];
        KSubset(a,5,s,0,0,3);
    
        return 0;
    }
    

答案 4 :(得分:0)

最直观的算法确实会使用递归。当你有一个集合时,我们假设你可以迭代它的所有元素。

如果我在元素e之后调用tail(e)一组所有元素。

因此我现在想要组合(s,k)

循环遍历s中的每个元素并获取e ::组合(tail(e),k-1)其中::表示“连接到每个”

当然有时候会有这样的组合(你不在列表的末尾)。

您只需要一个主集合(一组集)来添加您的组合以及创建

的方法

因此,假设我们没有任何全局变量,我们可以有类似的东西:

getCombinations( headset [in], tailset [in], count [in], output [append] )

耳机或尾部可能是空的。 count可以是0,在这种情况下我们将“耳机”写入输出(基本条件),否则我们遍历tailset中的每个元素,将其(本地)添加到耳机,使tailset(本地)成为我们元素的尾部,减去1从计数和“递归”调用函数。

答案 5 :(得分:0)

这是一些伪代码。如果调用值已经存在,您可以通过在递归调用检查之前存储每个调用的值来剪切相同的递归调用。

以下算法将包含除空集之外的所有子集。

list * subsets(string s, list * v){
    if(s.length() == 1){
        list.add(s);    
        return v;
    }
    else
    {
        list * temp = subsets(s[1 to length-1], v);     
        int length = temp->size();

        for(int i=0;i<length;i++){
            temp.add(s[0]+temp[i]);
        }

        list.add(s[0]);
        return temp;
    }
}

答案 6 :(得分:0)

 #include <stdio.h>
 #define FIN "subsets.in"
 #define FOUT "subsets.out"
 #define MAXSIZE 100

 void performSubsets(int n, int k){

 int i, j, s, v[ MAXSIZE ]; 

 freopen(FOUT, "w", stdout);


 memset(v, 0, sizeof( v ));

 do {

    v[ n - 1 ]++;

    for(i = n - 1; i >= 1; i--) {

        if(v[ i ] > 1) {

           v[ i ] -= 2;

           v[ i - 1 ] += 1;  
        }
    }

    s = 0;

    for(j = 0; j < n; j++) s += v[j];

    for(j = 0; j < n; j++) 

        if( v[ j ] && s == k) printf("%d ", (j + 1));

   if(s == k) printf("\n");

 } while(s < n);     

fclose( stdout );      

}

int main() {

    int n, k; 

    freopen(FIN, "r", stdin);

    //read n and size k
    scanf("%d %d", &n, &k);

fclose( stdin );

performSubsets(n,k);

}

使用非递归算法可以解决此问题。

答案 7 :(得分:0)

我的旧代码给出以下结果:

package runner;
import org.junit.runner.RunWith;

import cucumber.api.CucumberOptions; //shows error as The import cucumber.api cannot be resolved
import cucumber.api.junit.Cucumber;//shows error as The import cucumber.api cannot be resolved


    @RunWith(Cucumber.class)// shows error as Class<Cucumber> cannot be resolved to a type
    @CucumberOptions( //shows error as CucumberOptions cannot be resolved to a type
            features = {"features"} //the path of the feature files
            ,glue={"stepDefinitions"}
            )//the path of the step definition files

    public class testRunner{ 

    }

充分优化:

111000
110100
110010
110001
101100
101010
101001
100110
100101
100011
011100
011010
011001
010110
010101
010011
001110
001101
001011
000111

答案 8 :(得分:0)

由于 user843453 的帖子的编辑队列已满,我将创建一个新帖子,并解释其工作原理。我还对变量名称进行了一些更改,以便更清楚地了解算法在做什么。下面是代码:

#include<iostream>
#include<string>
#include<list>

using namespace std;

void print( list<int> l){
    for(list<int>::iterator it=l.begin(); it!=l.end() ; ++it)
            cout << " " << *it;
    cout<<endl;
}

void subset(int arr[], int size, int choices_left, int index, list<int> &l){
    if(choices_left==0){
        print(l);
        return;
    }
    for(int i=index; i<size;i++){
        l.push_back(arr[i]);
        subset(arr,size,choices_left-1,i+1,l);
        l.pop_back();
    }

}     

int main(){
    int array[5]={1,2,3,4,5};
    list<int> lt;   
    subset(array,5,3,0,lt);


    return 0;
}

要了解这是如何工作的,我们应该了解该示例调用中发生了什么:

A = [a0, a1, ..., a4]

ss(A, size = 5, choices_left = 3, index = 0, [])
|
|   i = 0
|   
|   [] ~> [a0]
|   
|   ss(A, size = 5, choices_left = 2, index = 1, [a0])
|   |
|   |   i = 1
|   |
|   |   [a0] ~> [a0, a1]
|   |   
|   |   ss(A, size = 5, choices_left = 1, index = 2, [a0, a1])
|   |   |
|   |   |   i = 2
|   |   |
|   |   |   [a0, a1] ~> [a0, a1, a2]
|   |   |
|   |   |   ss(A, size = 5, choices_left = 0, index = 3, [a0, a1, a2])
|   |   |   |   [a0, a1, a2] is printed
|   |   |   -------------------- POPPED ---------------------
|   |   |
|   |   |   [a0, a1, a2] ~> [a0, a1]
|   |   |
|   |   |   i = 3
|   |   A1
|   |   |   [a0, a1] ~> [a0, a1, a3]
|   |   |
|   |   |   ss(A, size = 5, choices_left = 0, index = 4, [a0, a1, a3])
|   |   |   |   [a0, a1, a3] is printed
|   |   |   -------------------- POPPED ---------------------
|   |   |
|   |   |   [a0, a1, a3] ~> [a0, a1]
C   |   |
|   |   |   i = 4
|   |   |
|   |   |   [a0, a1] ~> [a0, a1, a4]
|   |   |
|   |   |   ss(A, size = 5, choices_left = 0, index = 5, [a0, a1, a4])
|   |   |   |   [a0, a1, a4] is printed
|   |   |   -------------------- POPPED ---------------------
|   |   |
|   |   |   [a0, a1, a4] ~> [a0, a1]
|   |   |
|   |   -------------------- POPPED ---------------------
|   |       
|   B   [a0, a1] ~> [a0]
|   |   
|   |   i = 2
|   |
|   |   [a0] ~> [a0, a2]
|   |   
|   |   ss(A, size = 5, choices_left = 1, index = 3, [a0, a2])
|   |   |
|   |   |           ...
|   |   |
|   |   |   [a0, a2, a3] is printed
|   |   |
|   |   A2           ...
|   |   |
|   |   |   [a0, a2, a4] is printed
|   |   |
|   |   |           ...
|   |   |
|   |   -------------------- POPPED ---------------------
|   |       
|   |   [a0, a2] ~> [a0]
|   |
|   |            ...
|   |
|   -------------------- POPPED ---------------------
|
|   [a0] ~> []
|
|   i = 1
|   
|   [] ~> [a1]
|   
|   ...
-------------------- POPPED ---------------------

理解这一点的一个好方法是注意在大小为 3 的任何可能子集中,我们知道原始列表 A 的任何元素要么是该列表的一部分,要么不是该列表的一部分。< /p>

这个想法对应于将元素推到后面的代码部分,然后执行其他操作,然后删除该元素。

做“其他事情”的部分是最重要的部分,我们可以通过查看递归中的 A1 部分来弄清楚它做了什么,在递归的这个深度,我们得到了列表 { {1}} 并负责为我们的子集生成最后一个元素,或者我们可以将其重新表述为生成大小为 1 的有效子集,这些子集仅使用列表中 [a0, a1] 以后的元素。

我们必须从 index 开始的原因是为了不产生重复的情况。要完全理解我的意思,你需要理解这个算法的从左到右的过程。

在整个算法的第一次迭代中,它询问:

“假设这个元素在列表中,使用剩余元素生成所有可能的子集,现在假设这个元素不在列表中,使用剩余元素生成所有可能的子集”

一般来说,它的核心是“给定大小为 n - k 的有效子集,使用我尚未考虑使用 k 元素的元素生成有效子集”。这就是为什么我们只需要考虑从 index 开始的元素。

具体来说,我们可以看到递归级别 index 表示“假设 B 将成为我们子集的一部分,让我们找到大小为 2 的子集,然后将它们连接到列表的末尾a0

该递归级别的下一个调用会说“假设 [a0] 不是子集的一部分,但 a0 是子集的一部分,生成大小为 2 的子集,然后将它们邻接到列表的末尾 a1

最后再来一次,它会说“假设 [a1]a0 不是子集的一部分,但 a1 是子集的一部分......


备注

正如其他解决方案所指出的,您可以使用二进制数来生成子序列,因为如果我们有列表 a2,我们可以看到二进制数 1 0 1 1 1 可能是一个列表指定该索引处的元素是否将成为该子集的一部分,因此它将生成子集 A = [a0, a1, a2, a3, a4]

然后完成两种不同算法之间的连接,您可以看到该算法将通过这样做生成二进制:

[a0, a2, a3, a4]

答案 9 :(得分:-2)

这是一个迭代解决方案:

#include <stdio.h>
#include <stdlib.h>
void printer(int locations[],int a[],int r)
{
    int i;
    for(i=0; i<r; i++)
    {
        int x=locations[i];
        printf("%d ",a[x]);
    }
    printf("\n");
}
int main()
{
    int a[100000];
    int locations[1000];
    int i,n,r;
    printf("Enter N: ");
    scanf("%d",&n);
    printf("Enter K: ");
    scanf("%d",&r);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    for(i=0; i<r; i++)
        locations[i]=i;
    printer(locations,a,r);
    while(locations[0]<n-r)
    {
        for(i=r-1; i>0; i--)
        {
            if(locations[i-1]<n-r+i-1)
            {
                if(locations[i]<n-r+i)
                {
                    locations[i]++;
                    printer(locations,a,r);
                    break;
                }
                else
                {
                    locations[i-1]++;
                    int j;
                    for(j=i; j<r; j++)
                    {
                        locations[j]=locations[j-1]+1;
                    }
                    printer(locations,a,r);
                    break;
                }
            }
        }
    }
    return 0;
}