Median richtig ausgeben



  • Ich möchte Median berechnen .
    Dafür muss nach Aufgabe diesen Code aus der Vorlesung benutzen. Habe zum Teil
    versucht die Ausgabe des Mediums zu verbessern, aber irgendwie klappt das nicht ganz

    int main() {
    
       vector<double> vTemps{}; // Temperaturwerte
        double temp{0.0};
        while (cin >> temp)// beliebig viele einlesen
            vTemps.push_back(temp); // speichern
    
        // arithm. Mittel:
        double sum{0.0};
        for (unsigned int i{0U}; i < vTemps.size(); ++i)
            sum += vTemps[i];
        cout << "Temperaturmittel: "
               << sum / vTemps.size() << endl;
        // sortieren mit Funktion aus StdLib, Header algorithm
        sort(vTemps.begin(), vTemps.end());
    
        // Median ausgeben (nicht ganz korrekt)
        if(i==0 || vTemps.at(i-1)/2 != vTemps.at(i))// kann man so schreiben?
       cout << "Median: "
              << vTemps.at(i) << endl;
        return 0;
    }
    


  • wenn n := vTemps.size() ist, dann im Fall, daß n ungerade ist,

    vTemps[(n-1)/2]

    ausgeben, und wenn n gerade (und nicht-null) ist, dann gibt es mehrere Möglichkeiten; zum Beispiel den Mittelwert der zentralen Elemente

    (vTemps[n/2-1] + vTemps[n/2])/2



  • Hi Helenchen,

    Du machst immer noch den gleichen Fehler: Du nimmst einen Quelltextmüllhaufen wie er ist und versuchst Dich durchzuwursteln.
    Mache Dir zuallererst die Mühe, den gefundenen Quelltext ordentlich zu formatieren, So, dass er für Dich wie ein aufgeschlagenes Buch da liegt. Das ist keine vergeudete Zeit, sondern der erste Schritt auf dem Weg zum durchblicken, um den Quelltext für Dich zu erschließen. Warum initialisiertst Du Variablen in der Form
    double temp{0.0};
    double temp = 0.0; ist aus meiner Sicht übersichtlicher. Alle geschweiften Klammern in eine Ebene. Gerade für Anfänger ist es wesentlich übersichtlicher, wenn sie nicht suchen müssen wo das Gegenstück ist.
    und dann bring alles was dazwischen ist einheitlich um eine Einrücktiefe weiter nach rechts.
    Mit den Kommentaren must Du eine Lösung finden, die Dir entgegenkommt. Ich schiebe sie gerne ganz nach rechts raus. Es kann aber auch gut sein, sie im Gegensatz zu allem anderen ab Spalte 1 beginnen zu lassen.
    Auf alle Fälle so, dass sie nicht vom Blick auf den Quelltext ablenken.
    Und dann mal einen Blick in Wikipedia, was mit Median überhaupt gemeint ist.
    Auch würde ich keine englischen Begriffe (sum) für Variablen benutzen, sondern für meine eigenen Variablen deutsche Begriffe (Summe). Zum einen stechen sie damit besser als Deine raus, und zum anderen reduzierst Du mögliche Nebenwirkungen falls diese Begriffe schon irgendwie vorbelegt sind.
    Dann solltest Du Variablen, die Du auch später nach der ersten Schleife noch verwenden willst davor anlegen. Oder für jede Schleife eine eigene verwenden (i, j, k...)
    Ob das Einlesen so funktioniert weiss ich nicht so ganz, zu lange nicht mehr C++ gemacht, kann auch hier auf dem Tablet nicht gut probieren 😞
    Aber ein Hinweis, was eingegeben werden soll wäre schon schön.
    Ich bin mir aber auch nicht sicher, ob das abschicken einer leeren Eingabe die Abbruch-Bedingung
    while (cin >> temp) ausreichend zuverlässig erfüllt. Kann meiner Meinung nach systemabhängig sein.
    Völlig verhaspelt hast Du Dich dann mit der Medianausgabe.

    // Median ausgeben (nicht ganz korrekt)
        if(i==0 || vTemps.at(i-1)/2 != vTemps.at(i))// kann man so schreiben?
       cout << "Median: "
              << vTemps.at(i) << endl;
    

    irgendwie hast Du da versucht alles in eine Bedingung zu stopfen.
    Keine Ahnung, woher i seinen Wert haben soll. Einfach davon ausgehen, dass i noch aus der letzten for-Schleife einen Wert hat ist unsauber und in dem Fall nicht zutreffend.
    Du musst i aus vTemp.size() bestimmen.
    und dann musst Du drei Varianten Unterscheiden,

    1. der Verktor ist leer (i == 0)
    2. der Vektor hat eine ungerade Anzahl Elemente ( i%2 == 1 )
    3. der Vektor hat eine gerade Anzahl Elemente ( i%2 == 0 )
    und immer daran denken, welches Element ausgegeben wird resultiert aus dem Wer in der Klammer, was Du damit machst (z.B halbieren ergibt sich aus dem Wert außerhalb der Klammer.
    vTemps.at(i-1)/2 halbiert also den zurückgegebenen Wert. Aber Du wolltest vermutlich nicht die Hälfte, sondern den Wert auf halber Strecke, Wie das geht hat Zufallswert beschrieben.
    insgesamt musste die Ausgabe etwa so aussehen:

    if ( i == 0 )
    {
    // Meckern Vektor ist leer
    }
    else
    {
      if ( i%2 == 1 )
      {
    // mittleres Element ausgeben
      }
      else
      {
    // Mittelwert der beiden mittleren Elemente ausgeben
      }
    }
    

    Ich hoffe, ich konnte ein bisschen helfen und Licht in die Dinge bringen.

    Gruß Mümmel



  • muemmel schrieb:

    Hi Helenchen,

    Du machst immer noch den gleichen Fehler: Du nimmst einen Quelltextmüllhaufen wie er ist und versuchst Dich durchzuwursteln.
    Mache Dir zuallererst die Mühe, den gefundenen Quelltext ordentlich zu formatieren, So, dass er für Dich wie ein aufgeschlagenes Buch da liegt. Das ist keine vergeudete Zeit, sondern der erste Schritt auf dem Weg zum durchblicken, um den Quelltext für Dich zu erschließen. Warum initialisiertst Du Variablen in der Form
    double temp{0.0};
    double temp = 0.0; ist aus meiner Sicht übersichtlicher. Alle geschweiften Klammern in eine Ebene. Gerade für Anfänger ist es wesentlich übersichtlicher, wenn sie nicht suchen müssen wo das Gegenstück ist.
    und dann bring alles was dazwischen ist einheitlich um eine Einrücktiefe weiter nach rechts.
    Mit den Kommentaren must Du eine Lösung finden, die Dir entgegenkommt. Ich schiebe sie gerne ganz nach rechts raus. Es kann aber auch gut sein, sie im Gegensatz zu allem anderen ab Spalte 1 beginnen zu lassen.
    Auf alle Fälle so, dass sie nicht vom Blick auf den Quelltext ablenken.
    Und dann mal einen Blick in Wikipedia, was mit Median überhaupt gemeint ist.
    Auch würde ich keine englischen Begriffe (sum) für Variablen benutzen, sondern für meine eigenen Variablen deutsche Begriffe (Summe). Zum einen stechen sie damit besser als Deine raus, und zum anderen reduzierst Du mögliche Nebenwirkungen falls diese Begriffe schon irgendwie vorbelegt sind.
    Dann solltest Du Variablen, die Du auch später nach der ersten Schleife noch verwenden willst davor anlegen. Oder für jede Schleife eine eigene verwenden (i, j, k...)
    Ob das Einlesen so funktioniert weiss ich nicht so ganz, zu lange nicht mehr C++ gemacht, kann auch hier auf dem Tablet nicht gut probieren 😞
    Aber ein Hinweis, was eingegeben werden soll wäre schon schön.
    Ich bin mir aber auch nicht sicher, ob das abschicken einer leeren Eingabe die Abbruch-Bedingung
    while (cin >> temp) ausreichend zuverlässig erfüllt. Kann meiner Meinung nach systemabhängig sein.
    Völlig verhaspelt hast Du Dich dann mit der Medianausgabe.

    // Median ausgeben (nicht ganz korrekt)
        if(i==0 || vTemps.at(i-1)/2 != vTemps.at(i))// kann man so schreiben?
       cout << "Median: "
              << vTemps.at(i) << endl;
    

    irgendwie hast Du da versucht alles in eine Bedingung zu stopfen.
    Keine Ahnung, woher i seinen Wert haben soll. Einfach davon ausgehen, dass i noch aus der letzten for-Schleife einen Wert hat ist unsauber und in dem Fall nicht zutreffend.
    Du musst i aus vTemp.size() bestimmen.
    und dann musst Du drei Varianten Unterscheiden,

    1. der Verktor ist leer (i == 0)
    2. der Vektor hat eine ungerade Anzahl Elemente ( i%2 == 1 )
    3. der Vektor hat eine gerade Anzahl Elemente ( i%2 == 0 )
    und immer daran denken, welches Element ausgegeben wird resultiert aus dem Wer in der Klammer, was Du damit machst (z.B halbieren ergibt sich aus dem Wert außerhalb der Klammer.
    vTemps.at(i-1)/2 halbiert also den zurückgegebenen Wert. Aber Du wolltest vermutlich nicht die Hälfte, sondern den Wert auf halber Strecke, Wie das geht hat Zufallswert beschrieben.
    insgesamt musste die Ausgabe etwa so aussehen:

    if ( i == 0 )
    {
    // Meckern Vektor ist leer
    }
    else
    {
      if ( i%2 == 1 )
      {
    // mittleres Element ausgeben
      }
      else
      {
    // Mittelwert der beiden mittleren Elemente ausgeben
      }
    }
    

    Ich hoffe, ich konnte ein bisschen helfen und Licht in die Dinge bringen.

    Gruß Mümmel

    Mümmel auch wenn du eigentlich nur Delphi Progger bist hast du mir zu 100% geholfen Programm läuft wunderbar 😃



  • Hi Helenchen,

    helenchen schrieb:

    Mümmel auch wenn du eigentlich nur Delphi Progger bist...

    Nicht so despektierlich. Bin ja schließlich von c und c++ zu Delphi gkommen.
    Aber was soll man machen, wenn einem ein möglicher zukünftiger Arbeitgeber ein Delphi auf den Schreibtisch legt und einen damit erpresst, dass er androht Gehalt zu zahlen, wenn man an dem Schreibtisch Platz nimmt.

    Gruß Mümmel



  • Ich möchte noch erwähnen, dass sort mehr tut als nötig. Man muss ja nur den Median an der richtigen Stelle stehen haben. Wie die anderen Elemente sortiert sind, ist nicht so wichtig. Dafür gibt es nth_element . Das ist in der Regel schneller als eine komplette Sortierung besonders bei vielen Daten. nth_element hat eine lineare Laufzeit wohingegen sort eine Komplexität von O(n log n) hat.

    So könnte das dann aussehen:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <boost/optional.hpp>
    
    auto reorder_median(std::vector<double> & vec) -> boost::optional<double> {
    	if (vec.empty()) return boost::none;
    	auto s = vec.size();
    	auto c = vec.begin() + s/2;
    	std::nth_element(vec.begin(), c, vec.end());
    	if (s % 2 == 1) {
    		return *c;
    	} else {
    		std::nth_element(vec.begin(), c-1, c);
    		return 0.5 * (c[-1] + *c);
    	}
    }
    
    int main() {
    	try {
    		std::vector<double> data = {9,1,8,2,7,3,6,4,5};
    		double med = reorder_median(data).value();
    		std::cout << "Median: " << med << '\n';
    	} catch (std::exception const& x) {
    		std::cerr << "Oops: " << x.what() << '\n';
    	}
    }
    

Log in to reply