it-swarm.com.de

Erstellen aller möglichen k Kombinationen von n Elementen in C++

Es gibt n Personen, die von 1 bis n nummeriert sind. Ich muss einen Code schreiben, der alle verschiedenen Kombinationen von k Leuten aus diesen n erzeugt und druckt. Bitte erläutern Sie den verwendeten Algorithmus. 

34
Prannoy Mittal

Ich nehme an, Sie fragen nach Kombinationen im kombinatorischen Sinne (dh die Reihenfolge der Elemente spielt keine Rolle, daher ist [1 2 3] dasselbe wie [2 1 3]). Die Idee ist ziemlich einfach, wenn Sie die Induktion/Rekursion verstehen: Um alle K- Elementkombinationen zu erhalten, wählen Sie zunächst das erste Element einer Kombination aus einer vorhandenen Gruppe von Personen aus und "verketten" Sie dieses anfängliche Element mit allen möglichen Kombinationen von K-1 Personen, die aus Elementen erstellt wurden, die dem ursprünglichen Element folgen.

Nehmen wir als Beispiel an, wir möchten alle Kombinationen von 3 Personen aus 5 Personen zusammenstellen. Dann können alle möglichen Kombinationen von 3 Personen in Bezug auf alle möglichen Kombinationen von 2 Personen ausgedrückt werden:

comb({ 1 2 3 4 5 }, 3) =
{ 1, comb({ 2 3 4 5 }, 2) } and
{ 2, comb({ 3 4 5 }, 2) } and
{ 3, comb({ 4 5 }, 2) }

Hier ist C++ - Code, der diese Idee umsetzt:

#include <iostream>
#include <vector>

using namespace std;

vector<int> people;
vector<int> combination;

void pretty_print(const vector<int>& v) {
  static int count = 0;
  cout << "combination no " << (++count) << ": [ ";
  for (int i = 0; i < v.size(); ++i) { cout << v[i] << " "; }
  cout << "] " << endl;
}

void go(int offset, int k) {
  if (k == 0) {
    pretty_print(combination);
    return;
  }
  for (int i = offset; i <= people.size() - k; ++i) {
    combination.Push_back(people[i]);
    go(i+1, k-1);
    combination.pop_back();
  }
}

int main() {
  int n = 5, k = 3;

  for (int i = 0; i < n; ++i) { people.Push_back(i+1); }
  go(0, k);

  return 0;
}

Und hier ist die Ausgabe für N = 5, K = 3:

combination no 1:  [ 1 2 3 ] 
combination no 2:  [ 1 2 4 ] 
combination no 3:  [ 1 2 5 ] 
combination no 4:  [ 1 3 4 ] 
combination no 5:  [ 1 3 5 ] 
combination no 6:  [ 1 4 5 ] 
combination no 7:  [ 2 3 4 ] 
combination no 8:  [ 2 3 5 ] 
combination no 9:  [ 2 4 5 ] 
combination no 10: [ 3 4 5 ] 
52
dorserg

Von Rosetta-Code

#include <algorithm>
#include <iostream>
#include <string>

void comb(int N, int K)
{
    std::string bitmask(K, 1); // K leading 1's
    bitmask.resize(N, 0); // N-K trailing 0's

    // print integers and permute bitmask
    do {
        for (int i = 0; i < N; ++i) // [0..N-1] integers
        {
            if (bitmask[i]) std::cout << " " << i;
        }
        std::cout << std::endl;
    } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
}

int main()
{
    comb(5, 3);
}

Ausgabe

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

Analyse und Idee

Der springende Punkt ist, mit der binären Darstellung von Zahlen zu spielen Zum Beispiel ist die Zahl 7 im Binärformat 0111.

Diese binäre Darstellung kann also auch als Zuordnungsliste als solche angesehen werden:

Für jedes Bit i bedeutet, wenn das Bit gesetzt ist (d. H. 1), das i -te Element ansonsten nicht zugewiesen wird.

Durch einfaches Berechnen einer Liste aufeinander folgender Binärzahlen und Ausnutzung der binären Darstellung (die sehr schnell sein kann) wird ein Algorithmus zur Berechnung aller Kombinationen von N über k erstellt.

Die Sortierung am Ende (einiger Implementierungen) ist nicht erforderlich. Es ist nur ein Weg, das Ergebnis deterministisch zu normalisieren, d. H. Für gleiche Zahlen (N, K) und denselben Algorithmus wird dieselbe order von Kombinationen zurückgegeben

Weitere Informationen zu Zahlendarstellungen und ihrer Beziehung zu Kombinationen, Permutationen, Stromsätzen (und anderen interessanten Dingen) finden Sie unter Kombinatorisches Zahlensystem , Faktorennummernsystem

34
Nikos M.

In Python wird dies als itertools.combinations implementiert

https://docs.python.org/2/library/itertools.html#itertools.combinations

In C++ könnte eine solche Kombinationsfunktion basierend auf einer Permutationsfunktion implementiert werden.

Die Grundidee ist, einen Vektor der Größe n zu verwenden und nur k item auf 1 zu setzen, dann könnten alle Kombinationen von nchoosek durch Sammeln der k Items in jeder Permutation erhalten werden großer Raum, da die Kombination normalerweise sehr groß ist. Es ist besser, als Generator implementiert zu werden oder Funktionscodes in do_sth () einzufügen.

Codebeispiel:

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

using namespace std;

int main(void) {

  int n=5, k=3;

  // vector<vector<int> > combinations;
 vector<int> selected;
 vector<int> selector(n);
 fill(selector.begin(), selector.begin() + k, 1);
 do {
     for (int i = 0; i < n; i++) {
      if (selector[i]) {
            selected.Push_back(i);
      }
     }
     //     combinations.Push_back(selected);
         do_sth(selected);
     copy(selected.begin(), selected.end(), ostream_iterator<int>(cout, " "));
     cout << endl;
     selected.clear();
 }
 while (prev_permutation(selector.begin(), selector.end()));

  return 0;
}

und die Ausgabe ist

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

Diese Lösung ist eigentlich ein Duplikat mit Generieren von Kombinationen in c ++

7
Ning

Wenn die Nummer des Satzes zwischen 32, 64 oder einer maschinennativen Grundelementgröße liegen würde, können Sie dies mit einer einfachen Bit-Bearbeitung tun.

template<typename T>
void combo(const T& c, int k)
{
    int n = c.size();
    int combo = (1 << k) - 1;       // k bit sets
    while (combo < 1<<n) {

        pretty_print(c, combo);

        int x = combo & -combo;
        int y = combo + x;
        int z = (combo & ~y);
        combo = z / x;
        combo >>= 1;
        combo |= y;
    }
}

dieses Beispiel ruft die Funktion pretty_print () in der Wörterbuchreihenfolge auf.

Zum Beispiel. Sie möchten 6C3 haben und davon ausgehen, dass die aktuelle 'Combo' 010110 ..__ ist. Die nächste Combo MÜSSEN 011001 . 011001 ist: 010000 | 001000 | 000001

010000: fortlaufend 1s der LSB-Seite gelöscht . 001000: Setzen Sie den Wert 1 für die nächsten 1s der LSB-Seite . 000001: Verschieben Sie kontinuierlich 1s des LSB nach rechts und entfernen Sie das LSB-Bit.

int x = combo & -combo;

dies ergibt die niedrigste 1.

int y = combo + x;

dies eliminiert kontinuierlich 1s der LSB-Seite und setzt 1 auf der nächsten Seite (im obigen Fall 010000 | 001000).

int z = (combo & ~y)

dies gibt Ihnen die kontinuierliche 1s der LSB-Seite (000110).

combo = z / x;
combo >> =1;

dies ist für 'kontinuierlich 1s von LSB nach rechts verschoben und LSB-Bit entfernt'.

Der letzte Job ist also, OR zu dem oben genannten.

combo |= y;

Ein einfaches konkretes Beispiel:

#include <bits/stdc++.h>

using namespace std;

template<typename T>
void pretty_print(const T& c, int combo)
{
    int n = c.size();
    for (int i = 0; i < n; ++i) {
        if ((combo >> i) & 1)
            cout << c[i] << ' ';
    }
    cout << endl;
}

template<typename T>
void combo(const T& c, int k)
{
    int n = c.size();
    int combo = (1 << k) - 1;       // k bit sets
    while (combo < 1<<n) {

        pretty_print(c, combo);

        int x = combo & -combo;
        int y = combo + x;
        int z = (combo & ~y);
        combo = z / x;
        combo >>= 1;
        combo |= y;
    }
}

int main()
{
    vector<char> c0 = {'1', '2', '3', '4', '5'};
    combo(c0, 3);

    vector<char> c1 = {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
    combo(c1, 4);
    return 0;
}

ergebnis:

1 2 3 
1 2 4 
1 3 4 
2 3 4 
1 2 5 
1 3 5 
2 3 5 
1 4 5 
2 4 5 
3 4 5 
a b c d 
a b c e 
a b d e 
a c d e 
b c d e 
a b c f 
a b d f 
a c d f 
b c d f 
a b e f 
a c e f 
b c e f 
a d e f 
b d e f 
c d e f 
a b c g 
a b d g 
a c d g 
b c d g 
a b e g 
a c e g 
b c e g 
a d e g 
b d e g 
c d e g 
a b f g 
a c f g 
b c f g 
a d f g 
b d f g 
c d f g 
a e f g 
b e f g 
c e f g 
d e f g 
5
user7781254

Ich habe eine Klasse in C # geschrieben, um allgemeine Funktionen für das Arbeiten mit dem Binomialkoeffizienten zu behandeln. Dies ist die Art von Problem, unter das Ihr Problem fällt. Es führt die folgenden Aufgaben aus:

  1. Gibt alle K-Indizes in einem Nice-Format aus, und wählen Sie N in eine Datei. Die K-Indizes können durch aussagekräftigere Zeichenketten oder Buchstaben ersetzt werden. Diese Methode macht das Lösen dieser Art von Problem ziemlich trivial.

  2. Konvertiert die K-Indizes in den richtigen Index eines Eintrags in der sortierten Binomialkoeffiziententabelle. Diese Technik ist viel schneller als ältere veröffentlichte Techniken, die auf Iteration beruhen. Dazu wird eine mathematische Eigenschaft verwendet, die dem Pascalschen Dreieck innewohnt. Mein Papier spricht darüber. Ich glaube, ich bin der Erste, der diese Technik entdeckt und veröffentlicht hat.

  3. Konvertiert den Index in einer sortierten binomischen Koeffiziententabelle in die entsprechenden K-Indizes. Ich glaube es ist auch schneller als die anderen Lösungen.

  4. Verwendet die Methode Mark Dominus zur Berechnung des Binomialkoeffizienten, bei dem die Wahrscheinlichkeit, dass er überläuft, sehr viel geringer ist und mit größeren Zahlen funktioniert.

  5. Die Klasse ist in .NET C # geschrieben und bietet eine Möglichkeit, die mit dem Problem in Zusammenhang stehenden Objekte (sofern vorhanden) mithilfe einer generischen Liste zu verwalten. Der Konstruktor dieser Klasse benötigt einen bool-Wert namens InitTable, der bei true eine generische Liste für die zu verwaltenden Objekte erstellt. Wenn dieser Wert "false" ist, wird die Tabelle nicht erstellt. Die Tabelle muss nicht erstellt werden, um die 4 oben genannten Methoden auszuführen. Es werden Zugriffsmethoden bereitgestellt, um auf die Tabelle zuzugreifen.

  6. Es gibt eine zugehörige Testklasse, die zeigt, wie die Klasse und ihre Methoden verwendet werden. Es wurde ausführlich mit 2 Fällen getestet und es sind keine Fehler bekannt.

Weitere Informationen zu dieser Klasse und zum Herunterladen des Codes finden Sie unter Tablizing The Binomial Coeffieicent .

Es sollte ziemlich einfach sein, die Klasse nach C++ zu portieren.

Die Lösung Ihres Problems besteht darin, die K-Indizes für jeden N select K-Fall zu generieren. Zum Beispiel:

int NumPeople = 10;
int N = TotalColumns;
// Loop thru all the possible groups of combinations.
for (int K = N - 1; K < N; K++)
{
   // Create the bin coeff object required to get all
   // the combos for this N choose K combination.
   BinCoeff<int> BC = new BinCoeff<int>(N, K, false);
   int NumCombos = BinCoeff<int>.GetBinCoeff(N, K);
   int[] KIndexes = new int[K];
   BC.OutputKIndexes(FileName, DispChars, "", " ", 60, false);
   // Loop thru all the combinations for this N choose K case.
   for (int Combo = 0; Combo < NumCombos; Combo++)
   {
      // Get the k-indexes for this combination, which in this case
      // are the indexes to each person in the problem set.
      BC.GetKIndexes(Loop, KIndexes);
      // Do whatever processing that needs to be done with the indicies in KIndexes.
      ...
   }
}

Die OutputKIndexes-Methode kann auch verwendet werden, um die K-Indizes in eine Datei auszugeben. Sie verwendet jedoch eine andere Datei für jeden ausgewählten K-Fall.

2
Bob Bryan

Hier ist ein Algorithmus, mit dem ich dieses Problem gelöst habe. Sie sollten in der Lage sein, es zu ändern, um mit Ihrem Code zu arbeiten.

void r_nCr(const unsigned int &startNum, const unsigned int &bitVal, const unsigned int &testNum) // Should be called with arguments (2^r)-1, 2^(r-1), 2^(n-1)
{
    unsigned int n = (startNum - bitVal) << 1;
    n += bitVal ? 1 : 0;

    for (unsigned int i = log2(testNum) + 1; i > 0; i--) // Prints combination as a series of 1s and 0s
        cout << (n >> (i - 1) & 1);
    cout << endl;

    if (!(n & testNum) && n != startNum)
        r_nCr(n, bitVal, testNum);

    if (bitVal && bitVal < testNum)
        r_nCr(startNum, bitVal >> 1, testNum);
}

Sie sehen eine Erklärung, wie es funktioniert hier .

2
android927

Dies kann auch mit Backtracking durch Aufrechterhalten eines besuchten Arrays erfolgen.

void foo(vector<vector<int> > &s,vector<int> &data,int go,int k,vector<int> &vis,int tot)
{

    vis[go]=1;
    data.Push_back(go);
    if(data.size()==k)
    {
        s.Push_back(data);
        vis[go]=0;
    data.pop_back();
        return;
    }

    for(int i=go+1;i<=tot;++i)
    {
       if(!vis[i])
       {
           foo(s,data,i,k,vis,tot);
       }
    }
    vis[go]=0;
    data.pop_back();
}


vector<vector<int> > Solution::combine(int n, int k) {
   vector<int> data;
   vector<int> vis(n+1,0);
   vector<vector<int> > sol;
   for(int i=1;i<=n;++i)
   {
       for(int i=1;i<=n;++i) vis[i]=0;
   foo(sol,data,i,k,vis,n);
   }
   return sol;

}
0
Arpit Gupta

Hinter dem folgenden Link befindet sich eine generische C # - Antwort auf dieses Problem: Wie formatiert man alle Kombinationen aus einer Liste von Objekten? Sie können die Ergebnisse ziemlich einfach auf die Länge von k beschränken.

https://stackoverflow.com/a/40417765/2613458

0
jaolho