11

I want to generate all the subsets of size k from a set.

eg:-say I have a set of 6 elements, I have to list all the subsets in which the cardinality of elements is 3.

I tried looking for solution,but those are code snippets. Its been long since I have done coding,so I find it hard to understand the code and construct a executable program around it.

A complete executable program in C or C++ will be quite helpful. Hoping of an optimal solution using recursion.

Yu Hao
  • 111,229
  • 40
  • 211
  • 267
Kumar
  • 135
  • 1
  • 1
  • 3
  • 19
    Requests for "please give me a complete program" are usually met with hostility here. You should show your work, and illustrate exactly what you're having trouble with. – John Dibling Dec 29 '10 at 15:55
  • 3
    This appears to be written in the form of a homework problem ... – Zac Howland Dec 29 '10 at 15:59
  • Hint: How would you go about getting all subsets of cardinally of 3? You wouldn't just make random stabs at it, would you? Once you have that in mind, begin to write your program. – Neil Dec 29 '10 at 16:01
  • 3
    "Recursion" and "optimal" almost never belong in the same sentence. – R.. GitHub STOP HELPING ICE Dec 29 '10 at 16:30
  • Did you check the faq? http://stackoverflow.com/tags/algorithm/faq –  Dec 29 '10 at 16:54
  • possible duplicate of [Algorithm to return all combinations of k elements from n](http://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n) –  Dec 29 '10 at 16:55
  • @John Dibling: A common way to *give them a complete program* is to write something obfuscated like in @marcog answer, that cannot be submitted directly to the teacher. – ruslik Dec 29 '10 at 16:56
  • @Muggen GCC compiles it, but that doesn't mean it's valid C++. – moinudin Dec 29 '10 at 17:07

10 Answers10

19

Find a working code below

#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;
}
user843453
  • 211
  • 2
  • 6
16

Initialize a bit array with (1<<nbits)-1 and then use this algorithm:

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

For sets larger than the maximum integer size, you can still apply the same algorithm to your own type.

R.. GitHub STOP HELPING ICE
  • 195,354
  • 31
  • 331
  • 669
  • Can you please provide a use case? Just in case the link is down or something? –  Dec 14 '16 at 06:52
  • @Christian Check this : http://stackoverflow.com/questions/8281951/bit-hack-to-generate-all-integers-with-a-given-number-of-1s – ptntialunrlsd Mar 15 '17 at 07:52
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);
}
Sanjeevakumar Hiremath
  • 10,299
  • 3
  • 38
  • 46
moinudin
  • 117,949
  • 42
  • 185
  • 213
  • @Muggen: there are actually two of them. – ruslik Dec 29 '10 at 16:53
  • 2
    @Muggen I think @John's comment above says the reason quite well. – moinudin Dec 29 '10 at 16:57
  • it gives me `expected ‘;’, ‘,’ or ‘)’ before ‘=’ token` but don't worry. Since op tagged both `C` and `C++` I don't think he cares. +1 anw. The results looks correct. –  Dec 29 '10 at 17:12
  • A performance improvement would be to return immediately if `k - q > p - r`. This eliminates impossible configurations early. – Ben Ruijl Sep 26 '13 at 09:00
4

The Problem can be solved using recursion. We need to consider the following cases for recursion.

  1. The current element is chosen . Now we recursively choose the remaining k-1 elements from the remaining set .(inclusion)
  2. The current element is not chosen . Now we recursively choose k elements from the remaining set.(exclusion)

Following is a C++ program that demonstrates the above Algorithm.

#include<iostream>
#include<vector>

using namespace std;    

void computeSubsets(vector<vector<int> > & results, vector<int> const& nums, vector<int> & curSet, int idx, int size) {
    
    if (idx == size) {
        results.push_back(curSet);
        return ;
    }
    
    // Include
    curSet.push_back(nums[idx]);
    computeSubsets(results, nums, curSet, idx+1, size);
    curSet.pop_back();
    
    // Exclude
    computeSubsets(results, nums, curSet, idx+1, size);
}


vector<vector<int>> getSubsets(vector<int> const& nums) {
    vector<vector<int> > results;
    vector<int> temp;
    int size = nums.size();
    
    temp.reserve(size);
    results.reserve(1 << size);
    
    computeSubsets(results, nums, temp,  0, size);
    
    return results;
    
}

int main(){

    vector<int> nums = {1, 2, 3};
    auto subsets = getSubsets(nums);
    for (auto const& subset : subsets) {
        for (auto const& x : subset) {
            cout << x << " ";
        }
        cout << "\n";
    }

    return 0;
}
0

Here's some pseudocode. You can cut same recursive calls by storing the values for each call as you go and before recursive call checking if the call value is already present.

The following algorithm will have all the subsets excluding the empty set.

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;
    }
}
eldarerathis
  • 32,541
  • 9
  • 86
  • 93
kofhearts
  • 2,845
  • 5
  • 30
  • 65
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);

}

This problem can be solved using an algorithm non-recursive.

0

The most intuitive algorithm would indeed use recursion. When you have a set, we will assume you can iterate over all its elements.

If I call tail(e) a set of all the elements after element e.

Thus I want right now combinations(s,k)

loop over each element in s and get e :: combinations(tail(e), k-1) where :: means "concatenated to each of"

Of course sometimes there will be no such combinations (you are off the end of the list).

You just need a master collection (set of sets) to add your combinations to and a way to create

So assuming we have no globals anywhere we can have something like:

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

headset or tailset could be empty. count could be 0, in which case we write "headset" to output (the base condition) otherwise we iterate through each element in tailset, adding it (locally) to headset, make tailset (locally) the tail of our element, subtract 1 from count and "recurse" by calling the function.

CashCow
  • 29,087
  • 4
  • 53
  • 86
0

My old code gives the following result:

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

Enough optimized:

#include <iostream>

int firstPermutation(int n, int k) {
    return ((1 << k) - 1) << (n - k);
}

int shiftLast1(int a) {
    return (a - 1) ^ ((a^(a - 1)) >> 2);
}

int add1AfterLast1(int a) {
    return a | (((a^(a - 1)) + 1) >> 2);
}

int nextPermutation(int a) {
    if ((a & (a + 1)) == 0) {
        return 0;
    }

    if (a & 1) {
        return add1AfterLast1(nextPermutation(a >> 1) << 1);
    }
    else {
        return shiftLast1(a);
    }
}

int main() {
    int n = 6;
    int k = 3;
    int a = firstPermutation(n, k);
    do {
        for (int i = 0; i < n; i++) {
            std::cout << ((a >> (n - 1 - i)) & 1);
        }
        std::cout << std::endl;
    } while ((a = nextPermutation(a)));
}
k06a
  • 14,368
  • 10
  • 57
  • 97
0

Since the edit queue for user843453's post is full, I am going to create a new post with an explanation as to why this works. I have also made some changes to the variable names so that it's more clear what the algorithm is doing. Here is the code once more:

#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;
}

To understand how this works, we should understand what occurs in that sample call:

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 ---------------------

One nice way to understand this is by noting that in any possible subset of size 3, we know that any element of the original list A is either part of this list or it's not part of the list.

This idea corresponds to the section of the code which pushes the element to the back, then does something else, and then removes that element.

The part which does "something else" is the most important part, and we can figure out what it does by looking at section A1 in the recursion, at this depth in the recursion we are given the list [a0, a1] and are tasked with generating the last element for our subset, alternatively we can rephrase that as generating valid subsets of size one, which only use elements from the index onward in the list.

The reason why we must go from index onward is so that we do not generate duplicate situations. To fully understand what I mean you need to understand the left to right process of this algorithm.

On the first iteration of the whole algorithm it asks:

"Suppose this element is in the list, generate all possible subsets using the remaining elements, now suppose this element is not in the list, generate all possible subsets using the remaining elements"

And in general at it's core, it becomes "given this valid subset of size n - k, generate a valid subsets using elements I have not considered yet using k elements". This is the reason why we need to only consider elements from index onward.

Concretely we can see that recursion level B is saying "Assuming that a0 is going to be part of our subset, let's find subsets of size 2 and then adjoin them to the end of the list [a0].

The next call at that recursion level would say "Assuming that a0 is not part of the subset, but that a1 is part of the subset, generate subsets of size 2, and then adjoin them to the end of the list [a1].

Finally going one more time, it would say "Assuming that a0 and a1 are not part of the subset but that a2 is part of the subset ...


Remark

As the other solutions point out, you can use binary numbers to generate the sub sequences, simply because if we have the list A = [a0, a1, a2, a3, a4] we can see that the binary number 1 0 1 1 1 could be a list which specified whether an element at that index is going to be part of that subset, so it would generate the subset [a0, a2, a3, a4].

Then to finish of the connection between the two different algorithms, you can see that this algorithm would be generating binary by doing this:

    for(int i=index; i<size;i++){
        l.push_back(arr[i]); // THIS IS FLIPPING BIT AT INDEX I to a 1
        // THE BELOW GENERATES ALL POSSIBLE BINARY NUMBERS SO THAT WHEN ANY ONE IS APPENDED TO OUR CURRENT BINARY NUMBER IT'S LENGTH IS N
        subset(arr,size,choices_left-1,i+1,l); 
        l.pop_back(); // FLIPPING THE BIT AT INDEX I BACK TO 0
    }
cuppajoeman
  • 197
  • 2
  • 9
-2

Here is an Iterative solution :

#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;
}