Was ist an meiner Quicksort implementierung falsch?



  • 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