Generating N choose K Permutations in C++ [duplicate]
Asked Answered
U

3

6

I have a function that receives n and k to create all the possible permutations of n choose k, and while it works for most combinations like 5 choose 3 or 3 choose 2, it doesn't for for others like 4 choose 2. I need some help finding and understanding the bug. Thanks for looking.

The function:

void PermGenerator(int n, int k)    
{    
    int d[] = {1,2,3,4,5,6,7,8,9};  
    sort (d, d+n);  
    cout << "These are the Possible Permutations: " << endl;  
    do  
    {  
        for (int i = 0; i < k; i++)  
        {  
            cout << d[i] << " ";  
            if (i == k-1) cout << endl;  
        }  
    } while (next_permutation(d, d+n));  
}  

I'm using the next_permutation function. cplusplus

When I try 4 choose 2, I should be getting 12 permutations, instead I get this:

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

Whereas, 3 choose 2 works perfectly with 6 possible permutations:

1 2 
1 3   
2 1   
2 3   
3 1   
3 2             
Unalienable answered 25/2, 2015 at 5:25 Comment(0)
C
9

The first k values are repeated n-k factorial times. Here is an easy, although inefficient, way to avoid the repetition:

int Factorial(int n)
{
  int result = 1;
  while (n>1) {
    result *= n--;
  }
  return result;
}

void PermGenerator(int n, int k)
{
    std::vector<int> d(n);
    std::iota(d.begin(),d.end(),1);
    cout << "These are the Possible Permutations: " << endl;
    int repeat = Factorial(n-k);
    do
    {
        for (int i = 0; i < k; i++)
        {
            cout << d[i] << " ";
        }
        cout << endl;
        for (int i=1; i!=repeat; ++i)
        {
            next_permutation(d.begin(),d.end());
        }
    } while (next_permutation(d.begin(),d.end()));
}

However, there is an even easier and more efficient way to do it using std::reverse (from https://mcmap.net/q/414496/-library-function-for-permutation-and-combination-in-c)

void PermGenerator(int n, int k)
{
    std::vector<int> d(n);
    std::iota(d.begin(),d.end(),1);
    cout << "These are the Possible Permutations: " << endl;
    do
    {
        for (int i = 0; i < k; i++)
        {
            cout << d[i] << " ";
        }
        cout << endl;
        std::reverse(d.begin()+k,d.end());
    } while (next_permutation(d.begin(),d.end()));
}

The trick here is to realize that the last permutation is just the reverse of the first permutation, so by reversing the last n-k elements, you automatically skip to the last permutation of those elements.

Culberson answered 25/2, 2015 at 6:28 Comment(2)
Thank you! This did the trick. As MBo said above, I was outputting the first k elements of the array and I didn't see that.Unalienable
@Corghee: Note that there is an even easier and more efficient way to do it by using std::reverse. See https://mcmap.net/q/414496/-library-function-for-permutation-and-combination-in-cCulberson
G
3

You may use the following:

template <typename T>
void Combination(const std::vector<T>& v, std::size_t count)
{
    assert(count <= v.size());
    std::vector<bool> bitset(v.size() - count, 0);
    bitset.resize(v.size(), 1);

    do {
        for (std::size_t i = 0; i != v.size(); ++i) {
            if (bitset[i]) {
                std::cout << v[i] << " ";
            }
        }
        std::cout << std::endl;
    } while (std::next_permutation(bitset.begin(), bitset.end()));
}

Live example

Goodygoody answered 25/2, 2015 at 8:56 Comment(0)
O
1

You output first k members of every n! permutations. 4! = 24 permutations. First two permutations are

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

and you have got 1,2 and 1,2

To get combinations (4,2), you might, for example, use vector

 {0,0,1,1}

permute it, and output indexes of 1's

Oidea answered 25/2, 2015 at 6:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.