Creating All Possible K Combinations of N Items in C++

Creating all possible subset of k and m combinations of n items in C

It's more trickier than I primarly thinked.

Okay, so lets say you have "n" uniq element.
The total amout of uniq possibility is "n!" (so for 5 element, you have 120 possibility).

Let's say you want to make "group" of "k" number.

So if n = 5 and k = 2, you end up with your example :

{0, 1}, {2, 3}, {4}.

Now, that's where the fun begin :
In order to know if the current proposition is not a duplicate, you can discard every proposition for which the first number in every complete group is not sorted.

for example :

{0, 1}, {2, 3}, {4}.

here, 1 and 3 are useless because that not the first value of a complete group, and 4 is part of an incomplete group.
So what is interesting is

{0, ?}, {2, ?}, {?}.

Is 0, 2 sorted ? Yes, so you can keep this proposition.
That means that if you have

{2, 3}, {0, 1}, {4}.

It's no good, because

{2, ?}, {0, ?}, {?}.

2, 0 is not sorted.


If you have n = 6 and k = 2, then is

{0, 2}, {3, 4}, {1, 5}

valid ? No, because 0 3 1 is not sorted.
And you can see that

{0, 2}, {1, 5} , {3, 4}

is the valid sorted proposition.


Now, is there a possibility to calculate how many valid proposition we will have if we know n and k ?

Yes.

Maybe.
I think ...
I will update if I can found something.

Edit :

Aaaaaannnd, here an implementation. A little fun to do ...
It based on the previous algorithm, so of course if my algorithm is false, then this code is false.

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



#define N 5
#define K 2


void Comb_Display(int proposition[N])
{
printf("{");
for (int i = 0; i < N; ++i) {
if (i && i % K == 0) {
printf("} {");
}
printf("%d%s", proposition[i], (i && (i + 1) % K == 0) || i + 1 >= N ? "" : ", ");
}
printf("}\n");
}

bool Comb_Valid(int proposition[N])
{
int nbGroup = N / K;

if (nbGroup == 1) {
return (true);
}
for (int i = 0; i < nbGroup; i += K) {
if (proposition[i] > proposition[i + K]) {
return (false);
}
}
return (true);
}

void Comb_MakeMagicPlease(int numberAvailable[N], int proposition[N], int ind)
{
// We are at the end of the array, so we have a proposition
if (ind >= N) {
printf("%s : ", Comb_Valid(proposition) ? "OK" : " "); // O = Valide, ' ' = invalide
Comb_Display(proposition);
return;
}

// We scan "numberAvailable" in order to find the first number available
for (int i = 0; i < N; i++) {
if (numberAvailable[i] != -1) {
int number = numberAvailable[i];

numberAvailable[i] = -1; // We mark the number as not available

proposition[ind] = number;
Comb_MakeMagicPlease(numberAvailable, proposition, ind + 1);
numberAvailable[i] = number; // we mark the number as available
}
}
}

int main(void)
{
int numberAvailable[N];
int proposition[N];

for (int i = 0; i < N; ++i) {
numberAvailable[i] = i + 1;
}

Comb_MakeMagicPlease(numberAvailable, proposition, 0);
return 0;
}

Algorithm to return all combinations of k elements from n

Art of Computer Programming Volume 4: Fascicle 3 has a ton of these that might fit your particular situation better than how I describe.

Gray Codes

An issue that you will come across is of course memory and pretty quickly, you'll have problems by 20 elements in your set -- 20C3 = 1140. And if you want to iterate over the set it's best to use a modified gray code algorithm so you aren't holding all of them in memory. These generate the next combination from the previous and avoid repetitions. There are many of these for different uses. Do we want to maximize the differences between successive combinations? minimize? et cetera.

Some of the original papers describing gray codes:

  1. Some Hamilton Paths and a Minimal Change Algorithm
  2. Adjacent Interchange Combination Generation Algorithm

Here are some other papers covering the topic:

  1. An Efficient Implementation of the Eades, Hickey, Read Adjacent Interchange Combination Generation Algorithm (PDF, with code in Pascal)
  2. Combination Generators
  3. Survey of Combinatorial Gray Codes (PostScript)
  4. An Algorithm for Gray Codes

Chase's Twiddle (algorithm)

Phillip J Chase, `Algorithm 382: Combinations of M out of N Objects' (1970)

The algorithm in C...

Index of Combinations in Lexicographical Order (Buckles Algorithm 515)

You can also reference a combination by its index (in lexicographical order). Realizing that the index should be some amount of change from right to left based on the index we can construct something that should recover a combination.

So, we have a set {1,2,3,4,5,6}... and we want three elements. Let's say {1,2,3} we can say that the difference between the elements is one and in order and minimal. {1,2,4} has one change and is lexicographically number 2. So the number of 'changes' in the last place accounts for one change in the lexicographical ordering. The second place, with one change {1,3,4} has one change but accounts for more change since it's in the second place (proportional to the number of elements in the original set).

The method I've described is a deconstruction, as it seems, from set to the index, we need to do the reverse – which is much trickier. This is how Buckles solves the problem. I wrote some C to compute them, with minor changes – I used the index of the sets rather than a number range to represent the set, so we are always working from 0...n.
Note:

  1. Since combinations are unordered, {1,3,2} = {1,2,3} --we order them to be lexicographical.
  2. This method has an implicit 0 to start the set for the first difference.

Index of Combinations in Lexicographical Order (McCaffrey)

There is another way:, its concept is easier to grasp and program but it's without the optimizations of Buckles. Fortunately, it also does not produce duplicate combinations:

The set x_k...x_1 in N that maximizes i = C(x_1,k) + C(x_2,k-1) + ... + C(x_k,1), where C(n,r) = {n choose r}.

For an example: 27 = C(6,4) + C(5,3) + C(2,2) + C(1,1). So, the 27th lexicographical combination of four things is: {1,2,5,6}, those are the indexes of whatever set you want to look at. Example below (OCaml), requires choose function, left to reader:

(* this will find the [x] combination of a [set] list when taking [k] elements *)
let combination_maccaffery set k x =
(* maximize function -- maximize a that is aCb *)
(* return largest c where c < i and choose(c,i) <= z *)
let rec maximize a b x =
if (choose a b ) <= x then a else maximize (a-1) b x
in
let rec iterate n x i = match i with
| 0 -> []
| i ->
let max = maximize n i x in
max :: iterate n (x - (choose max i)) (i-1)
in
if x < 0 then failwith "errors" else
let idxs = iterate (List.length set) x k in
List.map (List.nth set) (List.sort (-) idxs)

A small and simple combinations iterator

The following two algorithms are provided for didactic purposes. They implement an iterator and (a more general) folder overall combinations.
They are as fast as possible, having the complexity O(nCk). The memory consumption is bound by k.

We will start with the iterator, which will call a user provided function for each combination

let iter_combs n k f =
let rec iter v s j =
if j = k then f v
else for i = s to n - 1 do iter (i::v) (i+1) (j+1) done in
iter [] 0 0

A more general version will call the user provided function along with the state variable, starting from the initial state. Since we need to pass the state between different states we won't use the for-loop, but instead, use recursion,

let fold_combs n k f x =
let rec loop i s c x =
if i < n then
loop (i+1) s c @@
let c = i::c and s = s + 1 and i = i + 1 in
if s < k then loop i s c x else f c x
else x in
loop 0 0 [] x

Fast algorithm for generating all combinations (n choose k) based on an initial input

I think the biggest problem you're going to encounter is not calculation but disk write speed or memory size. By the way, it seems you wrongly determined number of combinations for n = 250 and k = 6. Did you use uint64_t? My number is 244 140 625 000 000.

So for this number you need ~1.4 Petabyte (~1400 Tb) of memory. This is your main problem. If you have that much big hard drive, you'd better use memory mapping, when write. You may consider using several threads to write: each will write its own chunk of memory.

So, I think you should think of other ways for providing combinations for solving your actual goal.

A naive solution. Change std::ofstream with memory mapped object.

int main()
{
const constexpr uint8_t N = 250;
const constexpr uint8_t K = 6;
const constexpr uint64_t CombinationsCount = std::pow(N, K);
using TCombination = std::array<uint8_t, K>;

std::cout << CombinationsCount << std::endl;

std::ofstream file("output.txt");
TCombination c;
for (uint64_t i = 0; i < CombinationsCount; ++i)
{
auto I = i;
for (auto j = 0; j < K; ++j)
{
c[j] = I % N;
I /= N;
file << (int)c[j];
}
file << std::endl;
}

}

If you want to use threads, just divide CombinationsCount with cores number and give each thread a task to write from specific address of memory (offset).

You asked for a function-like solution. You can pass different names of files and use different threads. Buy you still need to use memory mapping.

const constexpr uint8_t N = 250;
const constexpr uint8_t K = 6;
const constexpr uint64_t CombinationsCount = std::pow(N, K);
using TCombination = std::array<uint8_t, K>;

void Generate(uint64_t start, uint64_t size, const char* fileName)
{
std::ofstream file(fileName);
TCombination c;
for (uint64_t i = start; i < start + size; ++i)
{
auto I = i;
for (auto j = 0; j < K; ++j)
{
c[j] = I % N;
I /= N;
file << (int)c[j];
}
file << std::endl;
}
}

int main()
{
std::cout << CombinationsCount << std::endl;

unsigned int threadsNum = std::thread::hardware_concurrency();

std::vector<std::thread> workers;
for (size_t i = 0; i < threadsNum; ++i)
workers.emplace_back(
Generate,
i * CombinationsCount / threadsNum,
CombinationsCount / threadsNum,
(std::string("output") + std::to_string(i)).c_str());

for (size_t i = 0; i < threadsNum; ++i)
workers[i].join();
}

All possible combinations and permutations of size n algorithm

Your task (all permutations of all combinations) can be easily solved using regular recursive function (as I did below) without any fancy algorithm.

Try it online!

#include <vector>
#include <functional>
#include <iostream>

void GenCombPerm(size_t n, size_t k, auto const & outf) {
std::vector<bool> used(n);
std::vector<size_t> path;
std::function<void()> Rec =
[&]{
if (path.size() >= k) {
outf(path);
return;
}
for (size_t i = 0; i < used.size(); ++i) {
if (used[i])
continue;
used[i] = true;
path.push_back(i);
Rec();
path.pop_back();
used[i] = false;
}
};
Rec();
}

int main() {
std::vector<size_t> a = {1, 2, 3};
GenCombPerm(a.size(), 2, [&](auto const & v){
std::cout << "[";
for (auto i: v)
std::cout << a[i] << ", ";
std::cout << "], ";
});
}

Output:

[1, 2, ], [1, 3, ], [2, 1, ], [2, 3, ], [3, 1, ], [3, 2, ], 

Creating a list of all possible combinations from a set of items for n combination sizes

A recursion can do the job. The idea is to choose a letter, print it as a possibility and combine it with all letters after it:

#include <bits/stdc++.h>    
using namespace std;

string letters[] = {"A", "B", "C", "D", "E"};
int alphabetSize = 5;
int combSizeLim = 3;

void gen(int index = 0, int combSize = 0, string comb = ""){
if(combSize > combSizeLim) return;
cout<<comb<<endl;
for(int i = index; i < alphabetSize; i++){
gen(i + 1, combSize + 1, comb + letters[i]);
}
}

int main(){
gen();
return 0;
}

OUTPUT:

A                                                                                                                                                                                  
AB
ABC
ABD
ABE
AC
ACD
ACE
AD
ADE
AE
B
BC
BCD
BCE
BD
BDE
BE
C
CD
CDE
CE
D
DE
E

Generating combinations in c++

A simple way using std::next_permutation:

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
int n, r;
std::cin >> n;
std::cin >> r;

std::vector<bool> v(n);
std::fill(v.end() - r, v.end(), true);

do {
for (int i = 0; i < n; ++i) {
if (v[i]) {
std::cout << (i + 1) << " ";
}
}
std::cout << "\n";
} while (std::next_permutation(v.begin(), v.end()));
return 0;
}

or a slight variation that outputs the results in an easier to follow order:

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
int n, r;
std::cin >> n;
std::cin >> r;

std::vector<bool> v(n);
std::fill(v.begin(), v.begin() + r, true);

do {
for (int i = 0; i < n; ++i) {
if (v[i]) {
std::cout << (i + 1) << " ";
}
}
std::cout << "\n";
} while (std::prev_permutation(v.begin(), v.end()));
return 0;
}

A bit of explanation:

It works by creating a "selection array" (v), where we place r selectors, then we create all permutations of these selectors, and print the corresponding set member if it is selected in in the current permutation of v. Hope this helps.



Related Topics



Leave a reply



Submit