2016-11-28 6 views
1

Mit der Funktion von Generate all sequences of bits within Hamming distance t erfüllt ist:rekursive Funktion Beenden, wenn ein dynamischer Zustand

void magic(char* str, int i, int changesLeft) { 
     if (changesLeft == 0) { 
       printf("%s\n", str); 
       return; 
     } 
     if (i < 0) return; 
     // flip current bit 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic(str, i-1, changesLeft-1); 
     // or don't flip it (flip it again to undo) 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic(str, i-1, changesLeft); 
} 

Ich möchte die rekursive Funktion beenden und mit dem Anrufer Funktion zurück, wenn eine bestimmte Bedingung auftritt (wenn es der Fall ist). Es ist also meine rekursive Funktion, wenn ich Stimmen höre, die ihr sagen könnten, dass sie aufhören soll!

Es geschieht nur nachstr gedruckt wird, hier:

if (changesLeft == 0) { 
    printf("%s\n", str); 
    int quit_now = voices(str); 
    return; 
} 

Wie dies (stoppen die Rekursion Entfaltung und Rückkehr in die Funktion Anrufer) tun?


Versuch:

if (i < 0 || quit_now == 1) return; 

scheint nur die Ausführung und nie enden zu blockieren!

PS - Ich bin auch in alten Methoden interessiert.

+0

Haben Sie versucht, goto: anstelle der Rückkehr? – hbagdi

+0

@hbagdi das schwarze Schaf? : O Ich habe es nie angerührt, aber wenn du eine Antwort postest, die beweist, dass es nützlich sein kann, wäre ich bereit zu .... Ich bin jetzt über 21 Jahre alt !!! =) // habe nicht verstanden, dass das Trolling war:/ – gsamaras

+1

Nicht. Sie trollen dich. – paddy

Antwort

1

Um es in der einfachsten Form zu setzen, könnten Sie so etwas tun:

void foo(bool & ret) { 
    // doStuff... 
    if (ret) return; 
    foo(ret); 
    // doStuff... 
    if (ret) return; 
    foo(ret); 
} 

Dann initiieren Sie die Rekursion:

bool ret = false; 
foo(ret); 

In Ihrem Fall, dass Sie die Rekursion brechen kann von

if (!changesLeft) { 
    printf("%s\n", str); 
    ret = true; 
    return; 
} 

Wenn Sie den Wert auf "true" setzen, werden Sie aus der gesamten Anrufstruktur entfernt.

Sie können es auch in C tun, verwenden Sie einfach einen Zeiger und nicht eine Referenz.

+1

Das ist eigentlich ähnlich wie ich "Ausnahmebehandlung" in C - vorausgesetzt, es ist mühsam, aber wenn Sie sauber und ordentlich Stapel abwickeln wollen, das ist der Weg zu gehen. longjmp wird hinter all den Dingen wühlen, die entlang der Stack-Frames passieren müssen, und du wirst eine schlimme Zeit haben. Es ist nur akzeptabel, wenn Sie nichts zu handhaben haben - keine Deallcationen, keine De-Initialisierungen und so weiter. – dtech

+0

Vorsicht, es kann Probleme mit der Speicherordnung geben und Rassenbedingungen, die auf den wahren Wert von "ret" zwischen Threads zugreifen, und eine geeignete Lösung kann die Verwendung eines Atomic-Operators beinhalten. – paddy

+0

@paddy das sieht wirklich nicht wie ein Multi-Thread-Szenario aus. Und Race Condition Protection in Multithread-Szenarien ist selbstverständlich. – dtech

3

Eine einfache Lösung, wenn Ihre Funktion derzeit keinen Rückgabewert hat, besteht darin, anzugeben, ob diese Abbruchbedingung erfüllt wurde. Dann können Sie damit alle rekursiven Aufrufe sofort beenden, wenn das Ergebnis wahr wird.

nicht sicher, ob ich Ihre erwartete Logik richtig hier bin erfassen, aber der intuitive Ansatz so etwas wie dieses würde:

int magic(char* str, int i, int changesLeft) { 
    int result; 
    if (changesLeft == 0) { 
      printf("%s\n", str); 
      return voices(str); 
    } 
    if (i < 0) return 0; 

    // flip current bit 
    str[i] = str[i] == '0' ? '1' : '0'; 
    result = magic(str, i-1, changesLeft-1); 

    if(!result) { 
     // or don't flip it (flip it again to undo) 
     str[i] = str[i] == '0' ? '1' : '0'; 
     result = magic(str, i-1, changesLeft); 
    } 

    return result; 
} 
+0

Und 'result' sollte seinen Wert bekommen ** right ** nach' printf ("% s \ n", str); ', wie ich gerne hätte, oder? – gsamaras

+0

Ja, in jedem Aufrufer, weil Sie 1 danach 'printf' zurückgeben. Und jeder Aufrufer auf dem Stapel beendet rekursive Aufrufe, wenn sie ein Ergebnis ungleich Null erhalten. Sie alle geben dieses Ergebnis zurück, um es den ganzen Weg zurück zu übergeben. Ein weiterer Vorteil ist, dass der erste Anrufer weiß, ob die Operation die Abbruchbedingung erfüllt hat oder nicht. Und so muss die Funktion selbst nicht das 'printf' haben. Man könnte sagen: 'if (magic (str, 0, x)) printf (" OK:% s \ n ", str); else printf ("No answer \ n"); ' – paddy

+0

Oh, tut mir leid, ich habe gerade erst gemerkt, dass ich das' '' '-' '-' -Spiel nicht behandelt habe. Kopieren/Einfügen und faules Lesen .... Ich habe aktualisiert. – paddy

1

Die magic Funktion ruft sich selbst rekursiv an zwei Stellen. Also müssen Sie an jedem dieser Orte Ihren Ausgangszustand überprüfen. Die Antwort von Paddy sagt dies aus.

Eine Alternative zum sofortigen Abwickeln des Stapels ist die Verwendung von setjmp und longjmp, die als nicht lokale goto fungieren können.

jmp_buf magic_buf; 

void magic_int(char* str, int i, int changesLeft) { 
     if (changesLeft == 0) { 
       printf("%s\n", str); 
       if (voices(str)) { 
        longjmp(magic_buf, 1); 
       } 
       return; 
     } 
     if (i < 0) return; 
     // flip current bit 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic_int(str, i-1, changesLeft-1); 
     // or don't flip it (flip it again to undo) 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic_int(str, i-1, changesLeft); 
} 

void magic(char* str, int i, int changesLeft) { 
    if (!setjmp(magic_buf)) { 
     magic(str, i, changesLeft); 
    } 
} 

Die setjmp Funktion gibt 0 wenn direkt aufgerufen. Wenn longjmp aufgerufen wird, ist es die setjmp Funktion, die tatsächlich zurückgibt, und der Rückgabewert ist der zweite Parameter, der an longjmp übergeben wird.

Hier haben wir eine Wrapper-Funktion, die setjmp aufruft. Damit wird der Sprungpunkt für den Zeitpunkt longjmp festgelegt. Dann wird die rekursive Funktion aufgerufen. Später, wenn die rekursive Funktion "hört Stimmen" sagt es jetzt zu beenden, ruft es longjmp, die sofort direkt auf die entsprechende setjmp Aufruf geht.

Diese Funktionen sind sowohl in C99 als auch in POSIX angegeben, daher sollte ein POSIX-konformes System (d. H. Linux) diese im C89-Modus noch verfügbar haben.

Wenn Sie dies in C++ tun würden, wäre die bevorzugte Methode, eine Ausnahme in die rekursive Funktion zu werfen und sie in der Wrapper-Funktion abzufangen.

struct magic_voices { 
    int rval; 
}; 

void magic_int(char* str, int i, int changesLeft) { 
     if (changesLeft == 0) { 
       printf("%s\n", str); 
       int rval = voices(str); 
       if (rval) { 
        struct magic_voices ex; 
        ex.rval = rval; 
        throw ex; 
       } 
       return; 
     } 
     if (i < 0) return; 
     // flip current bit 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic_int(str, i-1, changesLeft-1); 
     // or don't flip it (flip it again to undo) 
     str[i] = str[i] == '0' ? '1' : '0'; 
     magic_int(str, i-1, changesLeft); 
} 

void magic(char* str, int i, int changesLeft) { 
    try { 
     magic(str, i, changesLeft); 
    } catch (struct magic_voices ex) { 
     printf("rval=%d\n", ex.rval); 
    } 
} 
+0

Sind diese Babys Standard C/C++? – gsamaras

+0

@gsamaras Ja, siehe meine Bearbeitung. – dbush

+0

C und C++ sind zwei verschiedene Sprachen. Von expliziten Sprüngen in C++ wird dringend abgeraten. Vor allem für etwas so einfaches wie gewöhnliches Beenden einer Rekursion. Der Compiler kann frei entscheiden, wann er die Rendite optimieren möchte. Der Programmierer sollte sich unter normalen Umständen nicht mit diesem Detail befassen. – paddy

1

Dies ist eine nicht-rekursive Variante. Im Grunde erzeugt es alle ansteigenden Sequenzen 0 <= a[0] < ... < a[dist-1] < strlen(num) und setzt Bits bei entsprechenden Indizes zurück.

void print(const char* num, const vector<int>& a) { 
    size_t k = 0, n = strlen(num); 
    for (size_t i = 0; i < n; ++i) 
     if (k < a.size() && a[k] == i) { 
      cout << (num[i] == '0') ? '1' : '0'; 
      ++k; 
     } 
     else 
      cout << num[i]; 
    cout << endl; 
} 

void hamming(const char* num, size_t dist) { 
    assert(dist > 0); 
    vector<int> a(dist); 
    size_t k = 0, n = strlen(num); 
    a[k] = -1; 
    while (true) 
     if (++a[k] > n - dist + k) 
      if (k == 0) 
       return; 
      else { 
       --k; 
       continue; 
      } 
     else 
      if (k == dist - 1) { 
       print(num, a); 
       // conditional return here 
      } 
      else { 
       a[k+1] = a[k]; 
       ++k; 
      } 
} 

, die wie folgt verwendet werden kann:

int main() 
{ 
    hamming("0000", 2); 
    /* output: 
     1100 
     1010 
     1001 
     0110 
     0101 
     0011 
    */ 
} 

P. S. Danke an @ruakh für die Erwähnung einer fehlenden Optimierung im while - if Zustand.

+0

Alex, Entschuldigung für das Kommentieren nach so viel Zeit, aber würde das nicht "cout << (num [i] == '0')? '1': '0'; 'wird natürlicher als' cout << geschrieben (num [i] == '0')? '0': '1'; ', oder sollte es so sein, wie du es geschrieben hast? Oh, es muss so sein, wie du es geschrieben hast, aber warum? =) – gsamaras

+0

@gsamaras Der Vektor 'a' soll Indizes beibehalten, für die Bits invertiert werden müssen. Wenn also "a" den aktuellen Index "i" enthält, drucken wir "1" statt "0" und umgekehrt. Ansonsten drucken wir das Bit wie es ist (siehe 'else'-Teil). – AlexD

+0

Danke AlexD, ich bin sehr fasziniert von dem Algorithmus und versuche, seine Zeitkomplexität zu analysieren, wie [hier] (http://stackoverflow.com/questions/41086928/time-complexity-of-aniterative-algorithmus) . – gsamaras