Sortierverfahren
-
Hallo,
weiß nicht, ob das nun alles im richtigen Forum ist aber denke man wird mir helfen können habe folgende zusammengebastelt:
#include<iostream> #include<time.h> #include<stdlib.h> #include<math.h> #define MAX 50000 using namespace std; class CSort { private: int zahl[MAX]; int originalzahl[MAX]; public: void ziehen(); void kopieren(); void ausgeben(); void bubble_sort(); void quick_sort(int, int); void dir_einfuegen(); void dir_auswahl(); void shell_sort(); }; /*Hier werden die Zufallszahlen gezogen. Die Obergrenze ist unter MAX definiert (in diesem Fall 50000)*/ void CSort::ziehen() { for(int i=0; i<MAX;i++) originalzahl[i]=rand()%MAX; } /*Hier werden die Zufallszahlen kopiert, damit jedes Sortiervefahren die selben Zahlen nutzt und nicht die Zahlen benutzt werden, die ein anderes Sortierverfahren vorher schon sortiert hatte. Diese Funktion muss immer aufgerufen werden, bevor eine Sortierfunktion aufgerufen wird.*/ void CSort::kopieren() { for(int i=0; i<MAX;i++) zahl[i]=originalzahl[i]; } void CSort::bubble_sort() { int speicher; for(int j=0; j<MAX;j++) for(int i=0; i<MAX-1; i++) if(zahl[i]>zahl[i+1]) { speicher=zahl[i]; zahl[i]=zahl[i+1]; zahl[i+1]=speicher; } } void CSort::quick_sort(int links, int rechts) { int i=links,k=rechts,speicher, zahl_mitte; zahl_mitte=zahl[(links+rechts)/2]; do { while (zahl[i]<zahl_mitte) i++; while (zahl_mitte<zahl[k]) k--; if(i<=k) { speicher = zahl[i]; zahl[i] = zahl[k]; zahl[k] = speicher; i++; k--; } } while (i<=k); if(links<k) quick_sort(links,k); if(i<rechts) quick_sort(i, rechts); } void CSort::dir_einfuegen() { int k, speicher; for (int i=1; i<MAX; i++) { speicher=zahl[i]; k=1; while(speicher<zahl[k-1]) { zahl[k]=zahl[k-1]; k--; if(k==0) break; } zahl[k]=speicher; } } void CSort::dir_auswahl() { int min,j,speicher; for(int i=0;i<MAX-1;i++) { min=i; for(j=i+1;j<MAX;j++) if(zahl[j]<zahl[min]) min=j; speicher=zahl[i]; zahl[i]=zahl[min]; zahl[min]=speicher; } } void CSort::shell_sort() { int h=1,speicher,j,i; for(h=1;h<=MAX/9;h=h*3+1); for(; h>0;h/=3) for(i=h;i<MAX;i++) { speicher=zahl[i];j=i; while(j>=h&&zahl[j-h]>speicher) { zahl[j]=zahl[j-h]; j-=h; } zahl[i]=speicher; } } void CSort::ausgeben() { for(int i=0; i<MAX;i++) cout<<zahl[i]<<endl; } int main() { double zeitdauer_b, zeitdauer_q, zeitdauer_d, zeitdauer_h, zeitdauer_a, zeitdauer_s; clock_t anfang, ende; srand((unsigned)time(NULL)); time_t timer=time(NULL); CSort meineZahlen; meineZahlen.ziehen(); cout<<" "<<endl<<endl<<endl; cout.width(41); cout.fill('-'); cout<<""<<endl; meineZahlen.kopieren(); anfang=clock(); meineZahlen.quick_sort(0, MAX-1); ende=clock(); zeitdauer_q=(double)(ende-anfang)/CLOCKS_PER_SEC; cout<<"| QuickSort: | "<<zeitdauer_q<<" |"<<endl; cout.width(41); cout.fill('-'); cout<<""<<endl; meineZahlen.kopieren(); anfang=clock(); meineZahlen.bubble_sort(); ende=clock(); zeitdauer_b=(double)(ende-anfang)/CLOCKS_PER_SEC; cout<<"| BubbleSort: | "<<zeitdauer_b<<" |"<<endl; cout.width(41); cout.fill('-'); cout<<""<<endl; meineZahlen.kopieren(); anfang=clock(); meineZahlen.dir_einfuegen(); ende=clock(); zeitdauer_d=(double)(ende-anfang)/CLOCKS_PER_SEC; cout<<"| Insertionsort: | "<<zeitdauer_d<<" |"<<endl; cout.width(41); cout.fill('-'); cout<<""<<endl; meineZahlen.kopieren(); anfang=clock(); meineZahlen.dir_auswahl(); ende=clock(); zeitdauer_a=(double)(ende-anfang)/CLOCKS_PER_SEC; cout<<"| Selectionsort: | "<<zeitdauer_a<<" |"<<endl; cout.width(41); cout.fill('-'); cout<<""<<endl; meineZahlen.kopieren(); anfang=clock(); meineZahlen.shell_sort(); ende=clock(); zeitdauer_s=(double)(ende-anfang)/CLOCKS_PER_SEC; cout<<"| Shellsort: | "<<zeitdauer_s<<" |"<<endl; cout.width(41); cout.fill('-'); cout<<""<<endl<<endl<<endl; // meineZahlen.ausgeben(); }Die Ausgabe sieht so aus:
-----------------------------------------
| QuickSort: | 0.03 |
-----------------------------------------
| BubbleSort: | 61.04 |
-----------------------------------------
| Insertionsort: | 0 |
-----------------------------------------
| Selectionsort: | 16.98 |
-----------------------------------------
| Shellsort: | 0.08 |
-----------------------------------------Das ist bisher auch alles schön und ich bin stolz sowas geschafft zu haben... Nun würde ich aber gerne die Ausgabe sortiert haben.. z.B. das das schnellste Verfahren an erster stelle steht usw. Aber wie geht das? Wonach müsste ich denn überhaupt googleln? Weil die Werte sortieren würde ich vielleicht hinbekommen, aber die Namen sollten schon hinkommen.
Bitte mit einfacher Erklärung, hab nicht wirklich viel Ahnung...
-
Die Zeitdauer wird doch ein deinen Variablen gespeichert (zeitdauer_a usw.). Die gibts du jetzt nicht direktnach jeden Test aus sondern wartest bis alle durchlaufen sind. Dann verlgeichst du am Ende und gibst dort alle aus.
Ach ja grüße mir den Weseloh

-
ja, klingt ganz logisch aba wie ich des machen soll weiß ich imma noch nicht

-
struct Output { double zeitdauer; char *beschreibung; }; Output messung[5]; messung[0].beschreibung = "Quicksort"; messung[0].zeitdauer = zeitdauer_q; ...dann sortierst du das array nach zeitdauer und gibst es dann der sortierten Reihenfolge nach aus.
-
Hm,
warum struct?
warum output messung?
Was muss ich dann sortieren lassen? etwa messung?
-
struct Output { double zeitdauer; char *beschreibung; }; int main() { double zeitdauer_b, zeitdauer_q, zeitdauer_d, zeitdauer_h, zeitdauer_a, zeitdauer_s; clock_t anfang, ende; srand((unsigned)time(NULL)); time_t timer=time(NULL); CSort meineZahlen; meineZahlen.ziehen(); meineZahlen.kopieren(); anfang=clock(); meineZahlen.quick_sort(0, MAX-1); ende=clock(); zeitdauer_q=(double)(ende-anfang)/CLOCKS_PER_SEC; meineZahlen.kopieren(); anfang=clock(); meineZahlen.bubble_sort(); ende=clock(); zeitdauer_b=(double)(ende-anfang)/CLOCKS_PER_SEC; meineZahlen.kopieren(); anfang=clock(); meineZahlen.dir_einfuegen(); ende=clock(); zeitdauer_d=(double)(ende-anfang)/CLOCKS_PER_SEC; meineZahlen.kopieren(); anfang=clock(); meineZahlen.dir_auswahl(); ende=clock(); zeitdauer_a=(double)(ende-anfang)/CLOCKS_PER_SEC; meineZahlen.kopieren(); anfang=clock(); meineZahlen.shell_sort(); ende=clock(); zeitdauer_s=(double)(ende-anfang)/CLOCKS_PER_SEC; Output messung[5]; messung[0].beschreibung = "Selectionsort"; messung[0].zeitdauer = zeitdauer_a; messung[1].beschreibung = "Bubblesort"; messung[1].zeitdauer = zeitdauer_b; messung[2].beschreibung = "Insertionsort"; messung[2].zeitdauer = zeitdauer_d; messung[3].beschreibung = "Quicksort"; messung[3].zeitdauer = zeitdauer_q; messung[4].beschreibung = "Shellsort"; messung[4].zeitdauer = zeitdauer_s; char *chrtmp = NULL; int inttmp = 0; for(int i = 0; i < 5; i++) { for(int j = 0; j < 4; j++) { if(messung[j].zeitdauer > messung[j+1].zeitdauer) { inttmp = messung[j].zeitdauer; chrtmp = messung[j].beschreibung; messung[j].zeitdauer = messung[j+1].zeitdauer; messung[j].beschreibung = messung[j+1].beschreibung; messung[j+1].zeitdauer = inttmp; messung[j+1].beschreibung = chrtmp; } } } for(int i = 0; i < 5; i++) { cout << messung[i].beschreibung << '\t' << messung[i].zeitdauer << '\n'; } }Ich benutz das Struct um ein festes Zeit-Beschreibungs-Paar zu machen, ich also zu jeder Zeit weiss welcher Algorithmus das war. Ich sortier das ganze dann nach der Zeit und geb die Beschreibung aus.
Das struct kannst auch Hans-Peter nennen, ich habs halt Output genannt weil es ja zur Ausgabe dient. Und "messung" ist halt ein Array vom Typ Output. genau wie bei "int blabla[7]" blabla ein Array vom Typ int ist.
-
Hey,
danke...
*freu*Werd ich mal versuchen nachzubasteln

Danke nochmal!!!
-
Was noch geiler kommt, ist wenn Du Deine Sortierverfahren mittels Templates und Überladung so flexibel machst, dass Du sie für das Sortieren der Ausgabe und andere Dinge wiederverwenden kannst. Hier ein Beispiel dafür mit dem selection_sort:
#include <iostream> #include <string> #include <iterator> #include <vector> using namespace std; template<typename TIterator> inline void selection_sort(TIterator anfang, TIterator ende) { TIterator j, min; typename iterator_traits<TIterator>::value_type speicher; for(;anfang!=(ende-1);++anfang) { min=anfang; for(j=anfang+1;j!=ende;++j) if(*j<*min) min=j; speicher=*anfang; *anfang=*min; *min=speicher; } } template<typename TIterator, typename TKleiner> inline void selection_sort(TIterator anfang, TIterator ende, TKleiner kleiner) { TIterator j, min; typename iterator_traits<TIterator>::value_type speicher; for(;anfang!=(ende-1);++anfang) { min=anfang; for(j=anfang+1;j!=ende;++j) if(kleiner(*j,*min)) min=j; speicher=*anfang; *anfang=*min; *min=speicher; } } class StringUndInt { public: StringUndInt(string s="null", int i=0):m_s(s), m_i(i){} bool operator<(const StringUndInt &op2) const { return m_i<op2.m_i; } friend bool istStringKleiner(const StringUndInt &op1, const StringUndInt &op2) { return op1.m_s<op2.m_s; } friend ostream &operator<<(ostream &ausgabe, const StringUndInt &einStringInt) { return ausgabe<<einStringInt.m_s; } private: string m_s; int m_i; }; int main() { int nuemmerchen[]={32,5,4,54,6534}; StringUndInt test[]={ StringUndInt("drei", 3), StringUndInt("vier", 4), StringUndInt("zwei", 2), StringUndInt("eins", 1)}; vector<StringUndInt> vecTest(test, test+4); selection_sort(nuemmerchen, nuemmerchen+5); copy(nuemmerchen, nuemmerchen+5, ostream_iterator<int>(cout,"\n")); cout<<endl; selection_sort(test, test+4); copy(test, test+4, ostream_iterator<StringUndInt>(cout,"\n")); cout<<endl; selection_sort(vecTest.begin(), vecTest.end(), istStringKleiner); copy(vecTest.begin(), vecTest.end(), ostream_iterator<StringUndInt>(cout,"\n")); cout<<endl; system("Pause"); }
-
Gibt es evtl. für eine Liste (Doppelverkettet) eine fertige ausprogrammierte Quicksort Sortierfunktion.
Müsste nur die die Element (Variant Datentyp) sortieren -> also Pointer umkopieren - dies jedoch mit Quicksort
Vielen Dank für Eure Hilfe!
-
Soll ich die Quicksortierfunktion nun mit einem Array oder einer DVL (doppelt verkettete Liste) machen?
Was wäre eurer Meinung nach schneller, ich tippe mal auf eine unsortierte DVL.
-
c147258 schrieb:
Soll ich die Quicksortierfunktion nun mit einem Array oder einer DVL (doppelt verkettete Liste) machen?
Was wäre eurer Meinung nach schneller, ich tippe mal auf eine unsortierte DVL.quicksort ist auf einer einfach verketten liste schneller als auf einer doppelt verkettetn liste. und quicksort ist auf einem array noch schneller.
-
Danke für deine rasche Antwort.
Einstweilen sortiere ich das ARRAY, da die Liste etwas umständlicher ist - jedoch verbraucht das Array dann ebenfalls enorm Speicher wie die Liste.
Vorgang:
DVL - Elemente hinzüfügen
dann Elemente herauslesen - (alle) - in array kopieren
array sortieren
array ausgeben (mit direktem Indexzugriff)
-
volkard schrieb:
quicksort ist auf einer einfach verketten liste schneller als auf einer doppelt verkettetn liste. und quicksort ist auf einem array noch schneller.
Ich müsste nun Strings sortieren (char arrays - matrix)
Welcher Vorgang wäre da nun schneller?
Matrix - sortieren mittesl Quicksort -Speicherbedarf wird sehr groß sein da die Matrix nach dem längsten Element bestimmt wird oder? (beim erzeugen)
EVL (einfach verkettete Liste) - in jedes Element in ein char Array reinkopieren und dann beim sortieren mittels Quicksort (strcmp) die Pointer umkopieren.
Später müsste man zb. Item(19) eingeben und es soll das 19te Element ausgegeben werden - hier wäre natürlich wieder die Matrix schneller - bei der Liste müsste ich 19x den Pointer auf den nächsten kopieren...
Danke
-
Ich müsste nun Strings sortieren (char arrays - matrix)
Welcher Vorgang wäre da nun schneller?drei-wege-quicksort, ganz klar.
Matrix - sortieren mittesl Quicksort -Speicherbedarf wird sehr groß sein da die Matrix nach dem längsten Element bestimmt wird oder? (beim erzeugen)
EVL (einfach verkettete Liste) - in jedes Element in ein char Array reinkopieren und dann beim sortieren mittels Quicksort (strcmp) die Pointer umkopieren.
Später müsste man zb. Item(19) eingeben und es soll das 19te Element ausgegeben werden - hier wäre natürlich wieder die Matrix schneller - bei der Liste müsste ich 19x den Pointer auf den nächsten kopieren...
ich versteh dich überhaupt nicht. von welchen listen redest du? wollen wir uns nicht darauf einigen, einen std::vectorstd::string> zu sortieren? oder meinetwegen alle zeilen, die über cin ind programm kommen?
-
volkard schrieb:
ich versteh dich überhaupt nicht. von welchen listen redest du? wollen wir uns nicht darauf einigen, einen std::vectorstd::string> zu sortieren? oder meinetwegen alle zeilen, die über cin ind programm kommen?
// // EVL - String - Char // typedef struct LISTELEMENT_CHAR{ char *data; struct LISTELEMENT_CHAR *next; }ListElement_char;Danke...war zu vertieft das ich es vergessen habe das ich die Liste direkt beim reinschreiben sortieren könnte.
Also:
Eingabe (Übergabe von VB)
Datentyp VARIANT in eine doppelt verkettete Liste - unsortiert
nun die Strings (vorher konvertierung von BSTR nach Char
in eine einfach verkette Liste während des schreibens schon sortieren (oder erst nachher?)Danke
Eingabe und das mit der DVL ist schon fix.
Mit Vectoren hab ich leider keine Erfahrung

-
c147258 schrieb:
nun die Strings (vorher konvertierung von BSTR nach Char
in eine einfach verkette Liste während des schreibens schon sortieren (oder erst nachher?)nachher. beim einsortieren haste O(n^2) und nimmst insertion sort. beim nachher-sortieren haste O(n*log(n)), was ganz erheblich viel schneller bei vielen (sagen wir mal mehr als 100000 elemeten) ist.
Eingabe und das mit der DVL ist schon fix.
ok.
also die eingangsdaten sind als VARIANT in einer DVL.die schaufelst du ein eine EVL um und machst char* aus den VARIANTs. machste das nur, um zu sortieren, oder ist die EVL auch als ausgangsdaten gefordert? normaler wäre nämlich ein char**. also das, was zwischendurch in meiner funktion (siehe unten) benutzt wird.
ich gehe mal von einer EVL aus, die lauter char* als daten hat. leg einen char** an und kopiere alle char* der EVL dort rein. dann sortiere das zeigerfeld. dann kopiere die zeiger plump von anfang bis ende wieder in die liste rein.
das sortieren wäre optimal mit einem drei-wege-quicksort. aber naja, mir scheint beinahe, du legst keinen riesigen wert darauf, ein oder zwei wochen am besten verfahren zu knobeln und wärst über ein intro-sort (besser als einfaches quicksort) in 5 minuten ganz froh.
//ungetestet, nur so reingehackt. struct StrLess{ bool operator()(char const* a,char const* b){ return strcmp(a,b)<0; } }; void sortiereMeineEVL(ListElement_char* anfang){ //zählen int anzahl=0; for(ListElement_char* i=anfang;i!=0;i=i->next) ++anzahl; //feld anlegen char** feld=new (char*)[anzahl]; //reinkopieren anzahl=0; for(ListElement_char* i=anfang;i!=0;i=i->next){ feld[anzahl]=i->data; ++anzahl; } //blitzschnell sortieren sort(feld,feld+anzahl,StrLess); //rauskopieren anzahl=0; for(ListElement_char* i=anfang;i!=0;i=i->next){ i->data=feld[anzahl]=; ++anzahl; } //feld löschen delete[] feld; }
-
volkard (forum kaputt?) schrieb:
die schaufelst du ein eine EVL um und machst char* aus den VARIANTs. machste das nur, um zu sortieren, oder ist die EVL auch als ausgangsdaten gefordert?
das mach ich weil ich dachte das es schneller sei das ganze in eine Liste zu schreiben weil ich jedes mal nur ein Element anhänge - da ist die Liste ja bekanntlich schneller "hoff ich halt" - zudem ist ja auch dann das sortieren schneller (siehe deinen Post weiter oben)
volkard (forum kaputt?) schrieb:
normaler wäre nämlich ein char**. also das, was zwischendurch in meiner funktion (siehe unten) benutzt wird.
Danke...werd ich versuchen das ich es hinbekomme

volkard (forum kaputt?) schrieb:
aber naja, mir scheint beinahe, du legst keinen riesigen wert darauf, ein oder zwei wochen am besten verfahren zu knobeln und wärst über ein intro-sort (besser als einfaches quicksort) in 5 minuten ganz froh.
Jop, weil ich ab 31. Juli nicht mehr da bin -> Ferialarbeit

Werde deinen Code mal ausprobieren ..danke!
-
c147258 schrieb:
das mach ich weil ich dachte das es schneller sei das ganze in eine Liste zu schreiben weil ich jedes mal nur ein Element anhänge - da ist die Liste ja bekanntlich schneller "hoff ich halt" - zudem ist ja auch dann das sortieren schneller (siehe deinen Post weiter oben)
um ein element anzuhängen hat man bei einer liste die kosten von einem new. new ist aber gar nicht so extrem schnell in c++. zeitkomplexität O(n) mit großem faktor.
alternativ könntest du ein array nehmen, und jedesmal, wenn du ein objekt einfügst, einen um ein element größeres array mit new anlegen und alle alten elemente ins neue kopieren und das neue element ans ende schreiben. macht auch nur n mal operator new aufrufen. aber n*(n-1) mal muß ein zeiger kopiert werden. zeitkomplexität O(n^2), also grottenlahm.
alternativ könntest du ein array nehmen, das auch mal ein wenig größer ist, als du sofort brauchst. dan schreibst so lange ins array, bis es voll ist. dann legste ein array an, das DOPPELT so groß ist wie das alte und kopierst die elemete des alten rein und schreibst ab dann wieder hinten dran, bis auch das voll ist und läßt wieder wachsen...
so mußte für 1000000 elemete nur 20-mal wachsen lassen. also nur 20-mal operator new und nur maximal 2000000 mal einen zeiger kopieren. das ist also O(n) mit sehr kleinem faktor. es ist einfach schnell.zum vergelich zwischen listen und arrays ist also zu sagen, daß arrays einfach schneller sind. nur einen unterschied gibt's: die liste wächst immer gleich schnell. immer einmal new und gut ist's. das array wächst viel schneller, außer ganz selten, da muß das ganze array umkopiert werden. willste also in einem 3d-echtzeit-game was wachsen lassen, würde es beim array manchmal ruckeln, immer wenn das array mal wachsen muss. aber dazwischen wäre es geil schnell, was aber dem benutzer recht egal ist. er braucht gleichmäßige performance und kein ruckeln. wenn der benutzer eh waren muss, bis alles eingelesen ist, dann ist das array vorzuziehen, weil es schneller ist. und überhaupt, für den programmierer ist es auch einfacher. und du hast netterweise die daen dann gleich in einem array vorliegen, was optimal zum sortieren ist.
es gibt noch eine kombination, die noch mehr bringt. kleine arrays, sagen wir mal immer 4k. genauer 1023 nutzzeiger und einen verkettungszeiger. du machst das kleine array voll und wenn es voll ist, wirste, statt es wachsen zu lassen, ein neues kleines array anlegen und es mit dem alten array verketten. also eine verkettete liste, deren daten lauter kleine arrays sind. da haste nur alle 1023 einfügunen einmal new. also ungefähr nx. und braucht auch nix zu kopieren. das ist zum einlesen allein gesehen optimal. aber dann kommt ja wieder das sortieren. du müßtest entweder merge-sort nehmen, weil das für diese datenstruktur ungemein passend ist, würdest dabei aber, schätze ich mal, bei großen datenmengen doch langsamer als introsort sein, oder du müßtest, wie beim bisherigen plan erst umkopieren, und dann wäre die einfache array-lösung wieder besser.
-
Vielen Dank für Eure hilfe...
Muss jedoch eine kleine Statusänderung vornehmen:
Wollte heute BSTR in die Liste kopieren - jedoch werden diese immer wieder überschrieben - gleiche AdresseSomit werde ich Integer/Double Werte welche aus dem VARIANT Datentyp kommen in eine standard Liste reinschreiben - also mit push_front(VARIANT) - somit brauch ich nicht meine eigenen Fkt für die DVL/EVL verwenden und kann auf die Bibliothek zurückgreifen.
Wenn jedoch ein String kommt (sprich BSTR) aus VARIANT werd ich ihn ebenfalls in eine Liste kopieren - jedoch nicht als VARIANT sondern schon als CHAR *.
Bei den Integer/Double werten kopieren ich zur Zeit die ganzen Werte aus der liste in ein Array und dieses wird dann sortiert. Liste ist auch praktisch da ich dann sofort ein Array mit fixer Größe anlegen kann. -
Wie würde ich sonst die größe des Arrays bekommen (bzw. müsste jedes Mal mit malloc/allco Speicher frei geben um das nächste Element reinschreiben zu können..Bei den Strings/Char* werde ich Volkards Methode anwenden (wenn ich es hinbekomme :D) oder gibt es auch noch andere Vorschläge - einfachere/schnellere
hier müsste ich ja eine Matrix anwenden, jedoch hätte das auch keinen Sinn - da die Char * nicht immer genau gleich lang sind und somit würde unnötig Speicher freigegeben.Danke!
-
c147258 schrieb:
Somit werde ich Integer/Double Werte welche aus dem VARIANT Datentyp kommen in eine standard Liste reinschreiben - also mit push_front(VARIANT) - somit brauch ich nicht meine eigenen Fkt für die DVL/EVL verwenden und kann auf die Bibliothek zurückgreifen.
hmm..dacht ich zuerst das mit der Standard Liste - jedoch drängt die Zeit, und das alles wieder umzustellen :(...zudem kenn ich mich bei meiner Liste bzgl. Pointer zugriff besser aus - bei der anderen Liste müsste ich das ja mit einem Iterator machen

Danke