Was ist an meiner Quicksort implementierung falsch?



  • Ich weiß, dass es Quicksort in der Standardbibliothek gibt, aber trotzdem, was ist hier falsch?

    template<class Iter, class Ftor>
    struct partition
    {
    Iter operator()(Iter b,Iter e,Ftor f)
    {
    	Iter i=b-1;	//Suchzeiger
    	Iter j=e;	//dito
    	typename std::iterator_traits<Iter>::value_type v=*(e-1);
    	for(;;)
    	{
    		while(++i!=e&&f(*i,v));
    		while(--j>i&&f(v,*j));
    		if(i>=j)    //<--- darauf bezieht sich die 2. Frage
    			break;
    		std::iter_swap(i,j);
    	};
    	std::iter_swap(i,e-1);
    	return i;
    };
    };
    
    template<class Iter,class Ftor,class Ftor2>
    void quicksort(Iter begin,Iter end,Ftor f,Ftor2 p)
    {
    	if((end-begin)<2)
    		return;
    	Iter i=p(begin,end,f);
    	quicksort(begin,i-1,f,p);
    	quicksort(i+1,end,f,p);
    };
    

    btw, ist es möglich die if-Abfrage rauszumachen? Ich meine, es dürfte kaum Unterschied machen, aber wenn die Schleife darüber aussteigt ist sowieso schluss...



  • while(++i!=e&&f(*i,v));  <<--  :warning: Das Semikolon :warning: 
            while(--j>i&&f(v,*j)); <<--  :warning: und gleich noch mal das Semikolon :warning: 
            if(i>=j)    
    ....
    

    // Edit:
    Falls es net klar sein sollte. die Semikola sollten sicherlich nicht da sein.



  • Hä? Natürlich müssen die da hin. Wie stellst du dir denn den Ablauf vor? Es hätte doch gar keinen Sinn die Schleife so oft zu durchlaufen...
    /edit: Mal ganz abgesehen davon, dass das Fehlen der Abfrage unweigerlich zu einem Speicherzugriffsfehler füren muss...



  • Sry

    Hab mir das net so genau angschaut.



  • Kann jedem mal passieren...
    Das hilft mir aber net wirklich weiter...



  • Was passiert denn, bzw. was macht er falsch? Das könnte zumindest helfen den Fehler einzugrenzen. Spontan würde ich sagen, dass du deine Partition-Funktion nochmal überarbeiten solltest.



  • Naja, er sortiert falsch...
    Beispiel:

    Wie viele Elemente?
    25 
    Max. Groesse?
    35 
    Erzeuge Elemente!
    Benoetigte Zeit (in Sekunden): 0
    Ausgabe in ungeordneter Reihenfolge:
    [24][12][9][23][31][21][27][13][5][9][34][34][7][11][24][15][11][18][18][17][8][16][1][12][18]
    Beginne Sortiervorgang!
    Ausgabe in geordneter Reihenfolge:
    [8][5][9][1][15][9][11][12][13][12][7][18][16][17][18][18][11][24][23][24][21][34][27][31][34]
    Benoetigte Zeit (in Sekunden): 0
    

    Die Partitionierungsfunktion... Naja, woanders kann der Fehler ja kaum sein, aber ich find ihn halt nicht...



  • Wie wäre es mit einem kompletten Programm. Man hat immer so wenig Lust den Code selbst zu komplettieren.



  • Nun, das komplette Programm sieht in etwa so aus:

    #include <algorithm>
    #include <iterator>
    #include <utility>
    #include <ctime>
    #include <cstdlib>
    #include <iostream>
    
    int main()
    {
    	std::cout<<"Wie viele Elemente?"<<std::endl;
    	unsigned c;
    	std::cin>>c;
    	std::vector<int> vec;
    	std::cout<<"Max. Groesse?"<<std::endl;
    	int a;
    	std::cin>>a;
    	std::cout<<"Erzeuge Elemente!"<<std::endl;
    	std::time_t b;
    	std::time(&b);
    	std::srand(std::time(0));
    	for(unsigned i=0;i<c;++i)
    		vec.push_back(std::rand()%a+1);
    	std::time_t e;
    	std::time(&e);
    	std::cout<<"Benoetigte Zeit (in Sekunden): "<<std::difftime(e,b)<<std::endl;
    	std::cout<<"Ausgabe in ungeordneter Reihenfolge:"<<std::endl;
    	for(unsigned i=0;i<vec.size();++i)
    		std::cout<<"["<<vec[i]<<"]";
    
    	std::cout<<std::endl<<"Beginne Sortiervorgang!"<<std::endl;
    	std::time(&b);
    	quicksort(vec.begin(),vec.end(),std::less<int>(),partition());
    	std::time(&e);
    	std::cout<<"Ausgabe in geordneter Reihenfolge:"<<std::endl;
    
    	for(unsigned i=0;i<vec.size();++i)
    		std::cout<<"["<<vec[i]<<"]";
    
    	std::cout<<std::endl;
    	std::cout<<"Benoetigte Zeit (in Sekunden): "<<std::difftime(e,b)<<std::endl;
    	return 0;
    }
    

    Das war halt einfach ein "session-Mitschnitt". Eigentlich ist das Programm übersichtlicher, hab jetzt halt zusammenkopiert...
    Aber wie gesagt, der Fehler muss im Algorithmus liegen, das andere Sortierverfahren Funkionieren...
    /edit: Ich hoffe jetzt stimmts...



  • Wenn ich beide Teile zusammenfüge kompiliert es nicht, einerseits fehlen die Header und andererseits ist der Aufruf von Quicksort fehlerhaft.



  • Als erstes würde ich sagen:

    Iter j = e - 1;

    anstatt

    Iter j = e;



  • Weil?
    Wenn du gründlich guckst, wird dir nochwas auffallen: Iter i=b-1;
    Aber kurz darunter:

    for(;;)
    {
    	while(++i!=e&&f(*i,v));
    	while(--j>i&&f(v,*j));
                        //...
    

    Wie du siehst, werden die Variablen erst inkrementiert...



  • Du willst nicht das j auf das Pivotelement zeigt, nachdem es einmal dekrementiert wurde.

    Die beiden anderen Fehler sind:

    quicksort(begin,i,f,p);

    ist besser als:

    quicksort(begin,i-1,f,p);

    und

    while( (--j>i) && !f(*j, v) );

    ist besser als:
    while(--j>i&&f(v,*j));



  • Zuletzt würde ich b als das Pivotelement nehmen, da ich mir gar nicht sicher bin ob man b-1 überhaupt bilden darf.



  • Danke! Die Fehler sind so offensichtlich... Naja, hinterher ist man immer klüger... Nur Das letzte verstehe ich nicht.
    Nehmen wir mal an, es geht um std::less:

    while( (--j>i) && !f(*j, v) );
    //ist besser als:
    while(--j>i&&f(v,*j));
    //umformen (damits gleich aussieht)
    while(--j>i&&!f(*j,v));
    while(--j>i&&f(v,*j));
    //durch relationszeichen ersetzen:
    while(--j>i&&!(*j<v));
    while(--j>i&&v<*j);
    //vereinfachen:
    while(--j>i&&*j>=v);
    while(--j>i&&v<*j);
    //ein letztes mal umformen:
    while(--j>i&&*j>=v);
    while(--j>i&&*j>v);
    

    Macht das einen Unterschied? Ach, jetzt seh ichs, dass sind weniger Vertauschungen, oder?



  • Ja, es sollten weniger Vertauschungen sein.



  • Die Fehler hättest du auch mit Papier und Bleistift selber finden können. 🙂



  • is zwar keine wirkliche Hilfe, aber versuch doch mal ne
    vernünftige Benennung zu finden ..

    template<
             class Iterator, 
             class CompareFunctor
            >
    struct Partition {
        Iterator operator()(Iter begin, Iter end, CompareFunctor compare)
        {
            typedef typename std::iterator_traits<Iter>::value_type Value;
                // das ist zwar wirklich unnötig, ich finds irgendwie schöner so ..
    
            Iterator i = begin - 1;
            Iterator j = end;
    
            Value pivot = *(end - 1);
    
            while(i < j) {
                while(++i != j && compare(*i, pivot) < 0)
                    ;
    
                while(--j != i && compare(pivot, *j) > 0)
                    ;
    
                if(i != j) {
                    std::iter_swap(i, j);
                }
            }
    
            std::iter_swap(i, e - 1);
            return i;
        }
    };
    
    template<
             class Iterator, 
             class PartitionFunctor, 
             class CompareFunctor
            >
    void quicksort(Iterator begin, Iterator end,
                   PartitionFunctor partition, CompareFunctor compare)
    {
        if(end - begin < 2) {
            return;
        }
    
        Iterator pivot = partition(begin, end, compare);
        quicksort(begin, pivot - 1, partition, compare);
        quicksort(pivot + 1, end  , partition, compare);
    }
    

    Übrigens seit wann is es so verdammt 31337 kein using namespace ...; zu
    verwenden? Ich kann ja verstehen, wenns heißt man will sich den gobalen
    Namespace nicht verschmutzen, aber da gibts doch immer noch die Möglichkeit
    ein etwas lokaleres using zu benutzen ...


Anmelden zum Antworten