Permutationen in C++?

2 Antworten

  • Und wo in Deinem Code ist der PermutationsAlgoritmus?
  • Welchen möchest Du benutzen?

Die Frage ist nur rhetorisch... Ich nehme an Du bist von selbst auf Heaps Algorithmus gestoßen. und gleich die schlechte Nachricht...

Du kannst nicht innerhalb des laufenden Heap-Permutators unerwünschte Ergebnisse entfernen!

Egal ob die in meinen folgenden Demos verwendete rekursive Variante (oder iterierend) der jeweils nächste Schritt verwendet das Ergebniss des Vorherigen. Wenn Du eine Kombination nicht weiter verwendest , fällen die daraus folgenden Zweige der Baumstruktur ebenfalls weg.

Bild zum Beitrag

Das ist auch der Grund weshalb man einen Permutator mich Multithreaded laufen lassen kann aufteilen kann! 🤔 (stimmt nicht so ganz : OuelletHuttunen )

Allerdings kannst Du auch bei dem OuelletHuttunen (Indexed) keine Elemente ausschließen. Es gibt ergo keine Möglichkeit etwas zu entfernen, bevor die Arbeit beendet ist.

...und da komme ich zu Deinem Snipped. Du führst nur die Elemente auf welche ausgeschlossen werden sollen. . Da bleibe ja nicht zum permutieren... Deshalb nehme ich an du hast vor, aus den 24 Einzelelementen alle möglichen Kombinationen zu bilden dabei ist Dir entgangen, das auch C++ dich bei Kombinationen von 24 Elementen nicht retten kann.

24 Elemente ergeben 620448401733239439360000 Kombinationen!

24! = 1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20*21*22*23*24 = 620448401733239439360000

selbst wenn jede Kombination im Speicher Deines Rechner (ohne Verwaltungsinformationen) nur 24 Byte verbraucht, brauchst Du 13868102470038222,65625 GB Speicher!

...mal zum vergleich der Zahlen:

Netto Speichebedarf: 14890761641597746544640000
32GB RAM           : 34359738368
2TB SSD            : 2199023255552

...aber selbst bei 12 Elementen kommst Du ohne immensen Verwaltungsaufwand weder in Python noch in C++ zurecht mehr als 2GB kann kein normales Array ... https://stackoverflow.com/questions/52427788/allocating-an-array-greater-than-2gb-in-c-or-c . Naaj, der Rechner ist auch nich schnell genug selbst wenn Du C++ vewendest , unter mehreren Tagen geht bei bei 12 Elementen nichts! Bei der Zeitkalkulation von 24! seigt mein Umrechnungsroutine aus.... Soviel Zeit ist noch nicht mal seit dem Urknall vergangen! ein par Milliarden Jahre spielen da keine Rolle-

Es macht also keine relevanten Unteschied was für eine Programmiesprache ich verwende.... Ich setze auf Powershell das ist zwar nicht schnell, aber ich habe noch ein altes Script zum Anpassen rumliegen.

Ich verwende auch keine Zahlen , sondern Buchstaben (Kombinationen), Warum auch ob Buchstaben oder Zahlen ist völlig irrelvant.,

Erstmal ein kleine Demo, wie der Zeichentausch läuft...

Heap-Pemute.ps1

#colordefinition ansicolors  definieren 
$esc=[char]27
$green="$esc[92m"
$red="$esc[91m"
$resColor="$esc[0m"
$greenPattern='{0}$0{1}'-f $green,$resColor
$redPattern='{0}$0{1}'-f $red,$resColor
#ende colordefinition

function permute ([char[]]$str , $Size ){
    if($Size -eq 1){
        $str -join ''
    }
    else{
        $LastIndex = $Size-1
        $switch = $Size -band 1 -bxor 1  # 1 oder 0 , bitoperatinen sind schneller als  Modulo 2
        permute $str  $LastIndex
        foreach ($i in 0..($LastIndex-1) ){
            $currendPosition = $i*$switch
                Write-Host $( $($str -join '') -replace $str[$LastIndex], $redPattern  -replace $str[ $currendPosition], $greenPattern ) '--> ' -n
             #tausche elemete
            $str[$LastIndex], $str[ $currendPosition] = $str[ $currendPosition], $str[$LastIndex]
                Write-Host $( $($str -join '') -replace $str[ $currendPosition], $redPattern  -replace $str[$LastIndex], $greenPattern )
            permute $str $LastIndex
        }
    }
}

$myStr = 'abcd'
$permCount=iex($myStr.length..1 -join '*')

$stopwatch =  [system.diagnostics.stopwatch]::StartNew()
#start Permutation
$AllPermutations=permute $myStr ($myStr.Length)
$stopwatch.Stop()
"$($stopwatch.Elapsed.TotalMilliseconds)  Millisekunden"
Write-Host "$permCount Permutationen moeglich"
Read-Host 'Enter  um forzufahren'
$AllPermutations|% {$i=0}{'{0} {1}'-f $_ , (++$i)}

pause

Bild zum Beitrag

...und wie man ungewünschte Zeichen Kombinationen entfernt

Demo2.ps1

#colordefinition ansicolors  definieren 
$esc=[char]27
$green="$esc[92m"
$red="$esc[91m"
$resColor="$esc[0m"
$greenPattern='{0}$0{1}'-f $green,$resColor
$redPattern='{0}$0{1}'-f $red,$resColor
#ende colordefinition


function permute ([char[]]$str , $Size ){
    if($Size -eq 1){
        $str -join ''
    } else{
        $LastIndex = $Size-1
        $switch = $Size -band 1 -bxor 1 
        permute $str  $LastIndex
        foreach ($i in 0..($LastIndex-1) ){
            $currendPosition = $i*$switch
            $str[$LastIndex], $str[ $currendPosition] = $str[ $currendPosition], $str[$LastIndex]
            permute $str $LastIndex
}   }   }


$myStr = 'abcdefgh'
 #RegEx für die Buchsabenkombis, welche nicht zulässig sind  (🙄statt deiner Zahlenkombinationen)


$SchlechtePattern = 'ab|cd|ef|gh'


$permCount=iex($myStr.length..1 -join '*')
Write-Host "$permCount Permutationen moeglich"
$stopwatch =  [system.diagnostics.stopwatch]::StartNew()
$AllPermutations=permute $myStr ($myStr.Length)
$stopwatch.Stop()
"$($stopwatch.Elapsed.TotalMilliseconds)  Millisekunden"
"$($AllPermutations.Length) Permutationen gesamt gefunden"

"unzulaessigen Kobinatione: $($SchlechtePattern -replace '\|' , ' , ' )"
Read-Host 'Enter  zum Aussortieren  der schechten'

#die Schlechten aussortieren
$DieSchlechten = @()
$DieGuten =  $AllPermutations|%{$i=0}{
    if ($_ -match $SchlechtePattern ) {
        #die schlechten ins Kröpfchen,
            Write-Host Schlechte Kombination gefunden: $( $_ -replace $SchlechtePattern,$redPattern ) 
        $DieSchlechten += $_
    }
    else{
        #die  Guten ins Töpfchen...
        $_
    }
}


"$($DieSchlechten.Length) Elemente mit unzulaessigen Kobinatione"
"$($DieGuten.Length) zulaessige Elemente"
Read-Host 'Enter  um forzufahren'
$DieGuten|% {$i=0}{'{0} {1}'-f $_ , (++$i)}
pause

Bild zum Beitrag

8 Zeichen = 4 "schlechte" Kombis sind als Anschauung genug.

Überigens, ist der Flaschenhals bei Consolenprogrammen nicht die Operation selbst, sondern Die Anzeige auf dem Bildschirm

Du kannst ja mal 10 Buchstaben /5 Kombis verwenden:

$myStr = 'abcdefghij'
$SchlechtePattern = 'ab|cd|ef|gh|ij'

Die Kalkulation der Permutation (ohne Screenoutput) dauert vielleicht ne halbe Stunde, die Anzeige der "schlechten" Kombinationen will nicht Enden...! (dabei Bedarf der "AusSortier"-Code kaum Leistung)

Auf Rosetta Code findest Du Code für fast jede gängige Sprache...

Woher ich das weiß:eigene Erfahrung – Ich mach das seit 30 Jahren
 - (Code, Python, Programmiersprache)  - (Code, Python, Programmiersprache)  - (Code, Python, Programmiersprache)

Erzesel  06.02.2024, 09:43

PS:

Man kann innerhalb der Permutations-Funktion die unzulässigen Ergebnisse aussortieren. ...Aber erst, nach dem jeweils der aktuelle "Ast" komplett abgearbeitet ist.

Dies bringt keine Reduzierung der durchzuführenden Permutationen. Es wird lediglich innerhalb der Funktion entschieden, ob das Ergebnis gespeichert wird.

90% der benötigten Zeit entfallen auf die Anzeige... also die "Write-Host"-Zeilen innerhalb der Funktion (im Normalbetrieb) auskommentieren

update.ps1

clear-host

#colordefinition ansicolors  definieren 
$esc=[char]27
$green="$esc[92m"
$red="$esc[91m"
$resColor="$esc[0m"
$greenPattern='{0}$0{1}'-f $green,$resColor
$redPattern='{0}$0{1}'-f $red,$resColor
#ende colordefinition

function permute ([char[]]$str , $Size ){
    if($Size -eq 1){
        $result = $str -join ''
         #nichts anders als die  vorherige  Variante...
         #bringt keine wesentlich Beschleunigung, da an diesem Punkt 
         #bereits der gesamte Verzweigungsbaum abgearbeitet wurde  
        if ($result -notmatch $SchlechtePattern ) {
            Write-Host "behalte Ergebnis : $( $result -replace  $result,$greenPattern )"
             #die  Guten ...
            return $result
        } else {
            Write-Host "verwerfe Ergebnis: $( $result -replace $SchlechtePattern,$redPattern )" 
        }
    } else{
        $LastIndex = $Size-1
        $switch = $Size -band 1 -bxor 1 
        permute $str  $LastIndex
        foreach ($i in 0..($LastIndex-1) ){
            $currendPosition = $i*$switch
            $str[$LastIndex], $str[ $currendPosition] = $str[ $currendPosition], $str[$LastIndex]
            permute $str $LastIndex
        }
    }   
}


#Main
$myStr = 'abcdefgh'
$SchlechtePattern = 'ab|cd|ef|gh'
$permCount=iex($myStr.length..1 -join '*')
"unzulaessigen Kombinationen: $($SchlechtePattern -replace '\|' , ' , '  -replace $SchlechtePattern,$redPattern )"
"$permCount Permutationen moeglich"
sleep 3

$stopwatch =  [system.diagnostics.stopwatch]::StartNew()
$DieGuten = permute $myStr ($myStr.Length)
$stopwatch.Stop()

"$($stopwatch.Elapsed.TotalMilliseconds)  Millisekunden"
"$($DieGuten.Length) zulaessige Elemente gespeichert"
Read-Host 'Enter um anzeigen  der ermittelten zulässigen Kombis'
$DieGuten|% {$i=0}{'{0} {1}'-f $_ , (++$i)}
pause




0
std::array<int, 25> a;
std::iota(a.begin(), a.end(), 0);
do{
    for(std::pair<int, int>& forbidden : forbidden_pairs){
        bool found_forbidden = false;
        for(int i = 0; i < 12; ++i){
            if(forbidden.first == a[i]){
                if(found_forbidden){
                    ...
                }else{
                    found_forbidden = true;
                }
            }
        }
    }
} while(std::next_permutation(a.begin(), a.end()));

Geht evtl. auch effizienter. Das Durchsuchen nach den verbotenen Paaren kann man anders gestalten, evtl. sogar vorab nur Permutationen erzeugen, die diese gar nicht enthalten. Evtl. kann man auch einen passenden Comparator erzeugen oder sonstiges, um das ganze auf die Länge 12 zu optimieren.


alexx5678 
Fragesteller
 05.02.2024, 15:32

Danke für die schnelle Antwort, ich bin ein absoluter Anfänger in C++ also schon mal sorry für alles dumme was ich mach. Ich habe folgenden Code mal probiert aber er nimmt nur die Zahlen von 1 bis 12. Kann ich das irgendwie simpel ändern bzw deinen Code da hinzufügen/ersetzen?

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

using namespace std;
vector<int> createSubset(int start, int end, int k) {
    vector<int> subset;
    for (int i = start; i <= end && k > 0; ++i) {
        subset.push_back(i);
        --k;
    }
    return subset;
}

int main() {
    vector<pair<int, int>> verbotene_paare = {{1, 13}, {2, 14}, {3, 15}, {4, 16}, {5, 17}, {6, 18}, {7, 19}, {8, 20}, {9, 21}, {10, 22}, {11, 23}, {12, 24}};
    int z = 1;
    vector<int> zahlen = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
    vector<int> zahlenSubset = createSubset(1, 24, 12);
    do {
        bool isValid = true;
        for (auto pair : verbotene_paare) {
            if (find(zahlenSubset.begin(), zahlenSubset.end(), pair.first) != zahlenSubset.end() &&
                find(zahlenSubset.begin(), zahlenSubset.end(), pair.second) != zahlenSubset.end()) {
                isValid = false;
                break;
0
Destranix  05.02.2024, 15:37
@alexx5678

Du hast keinen Code gepostet der compilen würde. Das erstellen einen Subsets ist auch sinnlos, du nimmsteinfach nur die ersten 12 Zahlen des Arrays.

0
alexx5678 
Fragesteller
 05.02.2024, 16:20
@Destranix

ok danke für die Antwort. Ich habe noch eine Frage: Wie kann ich alle Permutationen der Zahlen 1 bis 24 (aber nur 12 Elemente je Permutation) generieren?

0
Destranix  05.02.2024, 16:42
@alexx5678

Da habe ich mir auch schon überlegt, wie das sinnvoll ginge.

Ist nicht ganz einfach, wenn du dafür std-Funktionen verwenden willst. Da müsste man evtl. eine besondere Datenstruktur oder einen besonderen Comparator verwenden.

Ansonsten kannst du aber natürlich auch selbst eine Permutationsfunktion schreiben.

Vielleicht findet sich auch etwas dazu im Internet.

0
Erzesel  05.02.2024, 23:31
@alexx5678
zahlen = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};

24! Bis zum Ende der Zeit...

selbst wenn jede Permutation nur einen Tick benötigen würde....

0
Erzesel  06.02.2024, 10:38

...nö das ist nur Theorie. Wenn man sich nicht mit Permutation beschäftigt hat, sieht die Nummer ganz so trivial aus.

Man kann keinen Ast absägen, ohne die Zweig verdorren zu lassen.

selbst ein Ast, der es auf den ersten Blick "würdig" wäre gekillte zu werden, kann normale "Zweige" haben.

0
Destranix  06.02.2024, 12:47
@Erzesel

Worauf beziehst du dich? Auf das Aussortieren verbotener Paare vor dem Erzeugen der Permutationen?

Folgender Pseudoalgorithmus:

Für alle Zahlenn im Range bilde Kombinationen bestimmter Länge ohne Beachtung der Reihenfolge
    Wenn Kombination enthält verbotenes Paar, dann gehe zur nächsten Kombination.
    Andernfalls bilde alle Permutationen für die gegebene Kombination.
0
Erzesel  06.02.2024, 16:48
@Destranix
vor dem Erzeugen der Permutationen?

Genau...

Ich habe mich vor langer Zeit mal eingehend mit der Materie auseinander gesetzt. Es wäre doch eine tolle Sache , wenn man bereits bereits vor Beginn der Permutation ausschließen bestimmte "Pfade" ausschließen könnte . Ich bin dabei natürlich auf die Nase gefallen. Weil's eben (aus oben beschriebenen Gründen) nicht gehen kann. In der nächsten Ebene und denen darunter, verschwindet die unerwünschte Kombinaton (größtenteils) wieder...

Immerhin würde die Pemutation von 24 Elementen (ibei ca 100microSec/tausch) 1,9 Billonen Jahre benötigen. Wenn das vom Fragesteller angedachte Vorgehen möglich wäre, würde die Anzahl der Permutationen auf 1/3 reduziert...

Ob 1,9Billion oder 600Mrd Jahre.... Das macht bei unser Lebensspanne keinen Unterschied....

Übrigens eignet sich Python , Powershell oder Javascript viel besser zum experimentieren, als C++. Und wenn ein Algorithmus mit wenigen Elementen in den ScriptSprachen funktioniert, ist es kein Problem diesen auf eine Compilersprache zu portieren .

Dass der Fragesteller wegen der "Langsamkeit" von Python auf C++ wechseln will, zeigt nur das er die Dimensionen des Themas völlig unterschätzt.

0
Destranix  07.02.2024, 11:15
@Erzesel

Und so wie in meinem Pseudocode geht es nicht?

Es werden klar nur die Permutationen wegggelassen, die verbotene Paare enthalten und klar alle anderen Permutationen weiterhin erzeugt.
Man schneidet dabei genau die Pfade weg, die man nicht haben will.

Das Erzeugen der Kombinationen ohne Beachtung der Reihenfolge könnte ein Problem sein. Einerseits erzeugt man in jedem Fall jeweils mindestens eine Kombination die die verbotenen Elemente enthält, um die Zweige, die auszusortieren sind zu identifizieren.

Andererseits gilt noch zu beweisen, dass das Erzeugen der Kombinationen ohne Beachtung der Reihenfolge geht ohne alle Permutationen zu Erzeugen.
Wenn ich aber mal davon ausginge, dass die Implementierung in der Python-Dokumentation zumindest theoretisch korrekt ist, so sollte das schneller gehen:

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

Es sei zudem zu erwähnen, dass sich das Problem ja vom Standard-Permutationsproblem in der Längenlimitierung unterscheidet. Ohne Längenlimitierung würde es zudem auch keinen Sinn machen, Permutationen auszuschließen, die bestimmte Paare enthalten, denn ohne Limitierung enthielte jede Permutation alle Paare.

0