2011-01-08 5 views
7

Wie kann ich alle möglichen Bitkombinationen in einem Array von Bits der Länge n generieren. Wenn ich mit allen Nullen in meinem Array anfange, gibt es n Möglichkeiten, das erste Bit zu platzieren, und für diese n Möglichkeiten gibt es n-1 Möglichkeiten, das zweite Bit zu platzieren. Einheit Alle n Bits sind auf eins gesetzt. Aber bis jetzt habe ich es nicht geschafft, es zu programmieren.Algorithmus zum Generieren aller möglichen Arrays von Einsen und Nullen einer gegebenen Länge

Auch viele Leute haben darauf hingewiesen, dass ich dies tun kann, indem ich von 0 bis (2^n) -1 zähle und die Zahl binär abdrucke. Dies wäre ein einfacher Weg, um das Problem zu lösen, aber in diesem Fall lasse ich die Maschine nur zählen, anstatt ihr zu sagen, wo sie platziert werden soll. Ich mache das für das Lernen, also würde ich gerne wissen, wie man den Ein-Platz-Ansatz programmiert.

+0

@Fred, wenn ich eine Lisp Antwort und eine C# Antwort wissen, bin ich hinzufügen „[Lisp]“ und „[C#]“ oder sollten wir ersetzen Sie einfach alle durch „[sprachunabhängig]“? –

+0

@Johannes: Ich würde gerne LISP und C# -Lösungen sehen :-) Ich habe das C++ - Tag hinzugefügt, weil Nils die Frage im C++ - Chat gepostet hat, also nahm ich an, dass er an einer C++ - Lösung interessiert war. Und ich habe den Haskell-Tag hinzugefügt, damit echte Haskell-Programmierer meine Haskell-Lösung verbessern konnten. – fredoverflow

Antwort

12

Wie würden Sie manuell auf Papier zählen? Du würdest die letzte Ziffer überprüfen. Wenn es 0 ist, setzen Sie es auf 1. Wenn es bereits 1 ist, setzen Sie es zurück auf 0 und fahren mit der nächsten Ziffer fort. Es ist also ein rekursiver Prozess.

Das folgende Programm erzeugt alle möglichen Kombinationen durch eine Sequenz mutiert:

#include <iostream> 

template <typename Iter> 
bool next(Iter begin, Iter end) 
{ 
    if (begin == end)  // changed all digits 
    {      // so we are back to zero 
     return false;  // that was the last number 
    } 
    --end; 
    if ((*end & 1) == 0) // even number is treated as zero 
    { 
     ++*end;   // increase to one 
     return true;  // still more numbers to come 
    } 
    else     // odd number is treated as one 
    { 
     --*end;   // decrease to zero 
     return next(begin, end); // RECURSE! 
    } 
} 

int main() 
{ 
    char test[] = "0000"; 
    do 
    { 
     std::cout << test << std::endl; 
    } while (next(test + 0, test + 4)); 
} 

Das Programm mit einer beliebigen Reihenfolge jeder Art funktioniert. Wenn Sie alle möglichen Kombinationen gleichzeitig benötigen, legen Sie sie einfach in eine Sammlung, anstatt sie auszudrucken. Natürlich benötigen Sie einen anderen Elementtyp, da Sie C-Arrays nicht in einen Vektor einfügen können. Werfen wir einen Vektor von Strings verwenden:

#include <string> 
#include <vector> 

int main() 
{ 
    std::vector<std::string> combinations; 
    std::string test = "0000"; 
    do 
    { 
     combinations.push_back(test); 
    } while (next(test.begin(), test.end())); 
    // now the vector contains all pssible combinations 
} 

Wenn Sie hier nicht Rekursion mögen, ist eine äquivalente iterative Lösung:

template <typename Iter> 
bool next(Iter begin, Iter end) 
{ 
    while (begin != end)  // we're not done yet 
    { 
     --end; 
     if ((*end & 1) == 0) // even number is treated as zero 
     { 
      ++*end;   // increase to one 
      return true;  // still more numbers to come 
     } 
     else     // odd number is treated as one 
     { 
      --*end;   // decrease to zero and loop 
     } 
    } 
    return false;    // that was the last number 
} 
+0

Dieser Algorithmus ist nicht rekursiv, sondern nur iterativ. Anstatt zu rekursiv zu arbeiten, könntest du einfach zum Anfang zurückspringen - natürlich unter Verwendung einer empfohlenen Kontrollstruktur :-) Ein intelligenter Compiler erkennt dies vielleicht, aber vielleicht nicht. Natürlich ist die Laufzeit exponentiell in n und das Stapelwachstum ist nur linear; Also wird das Programm funktionieren. Aber unnötige Rekursion ist im Allgemeinen eine schlechte Sache. – TonyK

+0

Ich frage mich, ob es einen iterativen Weg gibt? – Nils

+0

@Tony: Ich habe meine Antwort mit einer iterativen Lösung aktualisiert. – fredoverflow

0

FredOverflow befindet sich direkt im Allgemeinen.

jedoch für 1s & 0s solltest du besser erhöhen nur eine ganze Zahl von 0:

int need_digits = 10 
unsigned int i=0 
while (! i>>need_digits){ 
    # convert to binary form: shift & check, make an array, or cast to string, anything. 
    } 

... Ich denke, Sie werden nicht mehr als 32 Bits benötigen, oder Sie an die Kette mehrere haben würde Ganzzahlen .. und bleiben Sie bei der vorherigen Antwort :)

+1

+1 für die Einfachheit des Ansatzes. Diese Schleifenbedingung ist jedoch düster; Bitte benutzen Sie '(für i = 0; i <(1L << need_digits); i ++)' oder ähnliches! –

+1

Sie haben meine Frage nicht vollständig gelesen. – Nils

+0

zum Lernen sollten Sie besser versuchen, die Aufgabe nicht mit 1s & 0s zu begrenzen, versuchen Sie, Zahlen wie [a..c] [a..d] [a..z] und so zu erzeugen;) – kolypto

10

Solche Probleme sind trivial funktional gelöst. Um die Lösungen der Länge n zu finden, finden Sie zuerst die Lösungen der Länge n-1 und fügen dann '0' und '1' an diese Lösungen an, wodurch der Lösungsraum verdoppelt wird.

Hier ist ein einfaches rekursives Programm Haskell:

comb 0 = [[]] 

comb n = 
    let rest = comb (n-1) 
    in map ('0':) rest 
    ++ map ('1':) rest 

Und hier ist ein Testlauf:

> comb 3 
["000","001","010","011","100","101","110","111"] 
+23

'replicateM 3 [0,1 ] ' – sdcvvc

+0

@sdcwc: +1 Das ist * genau * warum ich den Haskell-Tag hinzugefügt habe :) – fredoverflow

+1

Also,' mapM (\ x -> [0, 1]) [1..n] ', was meiner Meinung nach ist) leichter zu verstehen, gibt alle Permutationen der Länge n von Bits. – danportin

1

A "wirklich" rekursive Ansatz in C++:

#include <iostream> 
#include <string> 

void print_digits(int n, std::string const& prefix = "") { 
    if (!n) { 
     std::cout << prefix << std::endl; 
     return; 
    } 
    print_digits(n-1, prefix + '0'); 
    print_digits(n-1, prefix + '1'); 
} 

int main(int, char**) { 
    print_digits(4); 
} 
1

Dies ist meine Antwort. Der Vorteil ist, dass alle Kombinationen in einem zweidimensionalen Array gespeichert werden, aber der Nachteil ist, dass Sie es nur für einen Stich bis zu 17 Ziffern verwenden können !!

#include <iostream> 

using namespace std; 

int main() 
    { 
    long long n,i1=0,i2=0, i=1, j, k=2, z=1; 
    cin >> n; 
    while (i<n){ 
     k = 2*k; 
     i++; 
    } 
    bool a[k][n], t = false; 
    j = n-1; 
    i1=0; 
    i2 = 0; 
    z = 1; 
    while (j>=0){ 
     if(j!=n-1){ 
     z=z*2; 
     } 
     i2++; 
     t = false; 
     i = 0; 
    while (i<k){ 
     i1 = 0; 
     while (i1<z){ 
      if(t==false){ 
      a[i][j]=false; 
      } 
      else { 
       a[i][j]= true; 
      } 
      i1++; 
      i++; 
     } 
     if(t==false){ 
      t = true; 
     }else { 
      t = false; 
     } 
    } 
    j--; 
    } 
    i = 0; 
    j = 0; 
    while (i<k){ 
     j = 0; 
     while (j<n){ 
      cout << a[i][j]; 
      j++; 
     } 
     cout << endl; 
     i++; 
    } 
    return 0; 
}