Laden...

[erledigt] Durchschnittsgeschwindigkeit berechnen (unbestimmte Anzahl von Werten)

Erstellt von Max89 vor 9 Jahren Letzter Beitrag vor 9 Jahren 4.221 Views
M
Max89 Themenstarter:in
2 Beiträge seit 2014
vor 9 Jahren
[erledigt] Durchschnittsgeschwindigkeit berechnen (unbestimmte Anzahl von Werten)

Hallo, liebe Community.

Mein Name ist Max und dies ist mein erster Beitrag hier im Forum. 🙂
Die :rtfm: Foren-Regeln habe ich natürlich gelesen!

Ich programmiere rein aus Interesse und habe vor ca. 8 Jahren mit Borland C++ und der VCL begonnen.
Seit ein paar Tagen bin ich nun (endlich) auf Visual Studio 2013 Express und C# .NET umgestiegen.

Soweit erstmal zu mir - falls noch jemand Fragen hat, immer her damit!
Nun aber erstmal zu meinem aktuellen Projekt:

Ich habe mir ein Programm mit folgenden Funktionen geschrieben:

  • Auslesen der aktuellen Geschwindigkeit aus dem Speicher von einem Spiel
    -> Timer (10 ms), OpenProcess, ReadProcessMemory, convert byte[] to float
  • Auswertung der Geschwindigkeit, Beschleunigung, zurückgelegten Distanz und durchschnittlichen Geschwindigkeit
    -> Timer (250 ms), Anzeige der Daten in einem ListView
  • Auswertung startet automatisch, sobald die Geschwindigkeit 2 km/h übersteigt
    -> Es wird jeweils eine neue Zeile im ListView angelegt und die Werte werden aller 250 ms aktualisiert

Dieses Projekt habe ich letzte Woche noch mit Borland C++ programmiert und zu Übungszwecken
nun nochmals mit Visual Studio 2013 und C# .NET nachprogrammiert.
Die Umstellung hat zwar etwas gedauert und war teilweise auch (erstmal) verwirrend, aber ich denke
ich konnte mich nun doch recht gut mit Visual Studio und C# einarbeiten. 🙂

Diesen Code verwende ich momentan zur Berechnung der durchschnittlichen Geschwindigkeit:


        // durchschnittliche Geschwindigkeit als Array 150x2 Werte
        // -> 450 km/h geteilt durch 3 km/h = 150 Items für mögliche Geschwindigkeiten
        // Wertepaare = Geschwindigkeit und Wertigkeit
        private float[,] fSpeedAverage = new float[150, 2];

        /// <summary>
        /// Berechnet die durchschnittliche Geschwindigkeit.
        /// - Es werden 150 Wertepaare (Geschwindigkeit, Wertigkeit) gespeichert.
        /// - Geschwindigkeit wird bei jeder Berechnung mit der Wertigkeit multipliziert.
        /// - ähnliche Werte werden zusammengefasst (3 km/h Toleranz)
        /// </summary>
        /// <param name="fSpeed">(float) Geschwindigkeit in km/h</param>
        private void LogAverage(float fSpeed)
        {
            // Zähler für die Anzahl der gespeicherten Werte
            int iCounter = 0;

            // wurde ein neuer Wert gespeichert bzw. ein alter Wert aktualisiert?
            bool bChanged = false;

            // gesamte Liste durchlaufen und prüfen ob die aktuelle Geschwindigkeit bereits gespeichert wurde
            for (int i = 0; i < fSpeedAverage.GetLength(0); ++i)
            {
                // aktuellen Wert mit der aktuellen Geschwindigkeit vergleichen
                // Toleranz liegt bei 3 km/h, dies entspricht +/- 1.5f
                if (!bChanged && fSpeedAverage[i, 0] != 0 && Math.Abs((float)(fSpeedAverage[i, 0] - fSpeed)) <= 1.5f)
                {
                    // nur die Wertigkeit erhöhen
                    fSpeedAverage[i, 1]++;

                    // Zähler erhöhen und Änderung speichern
                    iCounter++; bChanged = true;
                }

                // aktueller Wert ist leer (0)
                else if (!bChanged && fSpeedAverage[i, 0] == 0)
                {
                    // aktuelle Geschwindigkeit und Wertigkeit '1' eintragen
                    fSpeedAverage[i, 0] = fSpeed;
                    fSpeedAverage[i, 1] = 1;

                    // Zähler erhöhen und Änderung speichern
                    iCounter++; bChanged = true;
                }

                // Wert ist nicht 0 und liegt nicht innerhalb der Toleranz - Zähler erhöhen
                else if (fSpeedAverage[i, 0] != 0) iCounter++;

                // Wert ist 0 und es wurde bereits ein Wert gespeichert - Schleife verlassen
                else if (bChanged && fSpeedAverage[i, 0] == 0) break;
            }

            // --------------------------------------------------------------
            // Durchschnitt berechnen

            // durchschnittliche Geschwindigkeit
            float fSpeedAvg = 0.0f;

            // Anzahl der Werte (Wertigkeit)
            int iAnzahl = 0;
                
            // Alle Werte addieren und Wertigkeit beachten
            // (Counter wird vorsichtshalber nochmal mit dem maximalen Index verglichen)
            for (int i = 0; i < iCounter && iCounter <= fSpeedAverage.GetLength(0); ++i)
            {
                // Speed (gesamt) += (speed * wertigkeit)
                fSpeedAvg += ((float)fSpeedAverage[i, 0] * fSpeedAverage[i, 1]);

                // Anzahl der Werte (Wertigkeit) addieren
                iAnzahl += (int)fSpeedAverage[i, 1];
            }

            // Debug:
            // Text = "iCounter = " + iCounter.ToString() + " | iAnzahl = " + iAnzahl.ToString();

            // Wert durch die Anzahl der Werte (Summe aller Wertigkeiten) teilen
            fSpeedAvg = (float)(fSpeedAvg / iAnzahl);

            // durchschnittliche Geschwindigkeit in der Liste anzeigen
            // Parameter = float <Wert>, String <Format>, int <SubItem-Index der aktuellen Zeile>
            WerteAnzeigen(fSpeedAvg, "km/h", 11);
        }

Der Code wird aller 250 ms aufgerufen und macht auch genau das was er machen soll. 🙂_(Die Funktion 'WerteAnzeigen()' ist außerhalb deklariert und ist für das Zeichnen der Liste zuständig.)_

Meine Frage(n) wäre(n) nun:

  • Gibt es eine elegantere Lösung um die Durchschnittsgeschwindigkeit zu berechnen?
    -> Ich musste bisher noch nie den Durchschnitt einer unbestimmten Anzahl von Werten über einen unbestimmten Zeitraum berechnen.
    -> Daher war dies mein (2.) Versuch eine Lösung für dieses Problemchen zu finden.
  • Gibt es allgemeine Vorschläge oder Kritik zu meinem Code?
    -> Vorschläge und Tipps sind immer gern gesehen. Ich lerne gern etwas dazu!

Anmerkung:
Ich habe mich für die 3 km/h Toleranz entschieden um die Anzahl der Werte zumindest auf ein Maximum von 150 begrenzen zu können.

Liebe Grüße
Max

C
2.121 Beiträge seit 2010
vor 9 Jahren

Was bedeutet die Wertigkeit? Ist das die Zeitspanne in der der selbe Geschwindigkeitswert gilt? Ich verstehe den Code nicht ganz.

Ohne viel auf deinen Code einzugehen, die Durchschnittsgeschwindigkeit ist doch der insgesamt zurückgelegte Weg geteilt durch die dafür insgesamt benötigte Zeit.
Wenn du in der Log Methode Weg und Zeit aufsummierst, ist die schnell berechnet. Daher brauchst du rein für die Durchschnittsgeschwindigkeit kein Array von einzelnen Geschwindigkeiten halten.

2.207 Beiträge seit 2011
vor 9 Jahren
  • Gibt es allgemeine Vorschläge oder Kritik zu meinem Code?
    -> Vorschläge und Tipps sind immer gern gesehen. Ich lerne gern etwas dazu!

beachte bitte [Hinweis] Wie poste ich richtig? Punkt 4a) "Bittet nicht um Code-Reviews von längerem oder gar kompletten Quellcode."

(Ich würde in dem Code so ziemlich alles umstellen 😉 aber... )

...Erstens macht das sowieso jeder anders (jeder hat seinen Programmierstil). Zweitens würde das eine Endlosdiskussion hochbringen und drittens kannst du dich an die C# Coding Conventions (C# Programming Guide) halten, damit fährst du schon nicht schlecht.

Gruss

Coffeebean

189 Beiträge seit 2014
vor 9 Jahren

Hallo Max89,

falls du wirklich die Werte behalten musst / möchtest, dann schaue dir mal die generischen Collections an System.Collections.Generic Namespace (v.a. Dictionary<T> und List<T>).
Wenn du die Werte nicht einzeln benötigst, kannst du es so wie chilic es beschrieben hat machen bzw. über Summengeschwindigkeit/SummandenZahl.

Ezio

4.221 Beiträge seit 2005
vor 9 Jahren

Könntest Du das nicht ohne Array mit einem Counter und einem Decimal abfackeln.

Beispiel:

10+20+30+40+50=150 / 5 Messwerte = 30

Berechnung:

10 Count 1 = 10
20 Schnitt=Schnitt +((20-10)/Count) Count 2 = 15
30 Schnitt=Schnitt +((30-15)/Count) Count 3 = 20
40 Schnitt=Schnitt +((40-20)/Count) Count 4 = 25
50 Schnitt=Schnitt +((50-25)/Count) Count 5 = 30

Also immer Schnitt = Schnitt + ((NeuerWert-Schnitt)/NewCount))

Je mehr Messwerte Du hast, desto kleiner wird die Beeinflussung durch die Abweichung des neuen Messwertes zum Schnitt.

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

M
Max89 Themenstarter:in
2 Beiträge seit 2014
vor 9 Jahren

Vielen lieben Dank für die zahlreichen Antworten und Lösungsansätze.
Nachdem ich jetzt Eure Vorschläge gesehen habe, frage ich mich echt warum ich das so kompliziert gelöst habe. 8o
Manchmal denke ich halt viel zu kompliziert und "sehe den Wald vor lauter Bäumen nicht".

Da ich die Distanz auch über die Geschwindigkeit berechne, werde ich wohl zur Berechnung der Durchschnittsgeschwindigkeit die Summengeschwindigkeit/SummandenZahl verwenden.
-> Das ist ja auch genau das, was ich aktuell mache - nur das mein Weg halt unnötig kompliziert ist.
-> An dieser Stelle möchte ich noch erwähnen, dass ich die Zwischenwerte nicht benötige und somit der Weg mit Array ja Blödsinn ist.

Was bedeutet die Wertigkeit? Ist das die Zeitspanne in der der selbe Geschwindigkeitswert gilt? Ich verstehe den Code nicht ganz.

Die Wertigkeit steht in meinem Code für die Anzahl der gespeicherten Geschwindigkeit.
Wenn also 120 km/h 2x gemessen wird, steht im Array: 120|2
Das funktioniert natürlich, ist aber - wie erwähnt - einfach nur umständlich.

beachte bitte
>
Punkt 4a) "Bittet nicht um Code-Reviews von längerem oder gar kompletten Quellcode."

Ich hatte diesen Punkt zwar gelesen und auch verstanden, aber scheinbar hatte ich das "längerem Quellcode" falsch gedeutet.
Ich war der Meinung man sollte halt nicht den gesamten Quelltext von etlichen Funktionen posten.
Ich entschuldige mich dafür und werde dies in Zukunft vermeiden!

Liebe Grüße
Max

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo Max89,

ob man Geschwindigkeiten einfach aufsummieren und anschließend durch die Anzahl der Messwerte teilen kann, hängt davon ab, ob die Messungen im konstanten Zeitintervallen oder in konstanten Steckeninvervallen gemessen wurden.

Wenn ich eine Stunde 100km/h fahre und eine weitere Stunde 200km/h, dann habe ich 100km+200km = 300km in zwei Stunden zurückgelegt, bin also tatsächlich im Mittel 150km/h gefahren.

Wenn ich aber 150km lang mit 100km/h fahre und danach nochmal 150km mit 200km/h, dann haben ich für 300km 1h30 + 45min = 2h15 benötigt, bin also im Schnitt nur mit 133km/h unterwegs gewesen.

Wenn die Geschwindigkeitsmessung außerdem nur zu bestimmten Zeitpunkten oder nur an bestimmten Streckenpunkten erfolgt, kommt es außerdem darauf an, wie sich die Geschwindigkeiten zwischen den Messpunkten geändert hat: linear, quadratisch, logarithmisch, schwankend, sprunghaft oder wie auch immer.

Da man das vielfach nicht sagen können wird, wäre es am sichersten, wenn man bei Messungen in festen Zeitabständen immer die zurückgelegte Strecke seit der letzten Messung ermittelt bzw. wenn man bei Messungen an festen Streckenabschnitten immer die dafür benötigte Zeit ermittelt. Nur daraus ergibt sich die exakte Durchschnittsgeschwindigkeit für das jeweils abgelaufene (Zeit- oder Strecken-)Intervall.

Für die Bildung des Gesamtdurchschnitts gilt dann das bereits Gesagte.

herbivore

189 Beiträge seit 2014
vor 9 Jahren

Hallo Max89,
vom Prinzip hat herbivore absolut recht.
Falls du die Strecke direkt aus dem Spiel bekommst, solltest du eher mit dieser Methode arbeiten.
Falls das nicht geht und geringe Abweichungen nicht so schlimm sind, dann kannst du die bisherige Methode weiter verwenden.
Die Bedingung ist, dass die Zeit zwischen den Messungen hinreichend hoch sein muss.
-> Falls du nicht gerade sehr hohe Geschwindikeiten misst (also z.B. von Projektilen, hypergalaktischen Antrieben, ...) sondern eher Fahrzeuggeschwindigkeiten, dann sollte die mit 10ms zeitdiskrete Kurve die reale Kurve hinreichend genau approximieren.

Was noch zu beachten ist, ist die Frage, ob du die Durchschnittsgeschwindigkeit immer nur über eines der 250ms Intervall brauchst oder ob du die gesamte Durchschnittsgeschwindigkeit über den gesamten Zeitraum haben möchtest. In ersterem Fall reicht Summengeschwindikeit/SummandenZahl. In zweitem Fall ist die Methode von Programmierhans anzuwenden, da

(BisherigerDurchschnitt+IntervallDurchschnitt)/2 != Gesamtdurchschnitt

Die Formel wäre dann (von Programmierhans abgeleitet)

Durchschnitt = Durchschnitt + ( SummeÜberIntervall + IntervallLänge * Durchschnitt)/GesamtZahlMesswerte

Ezio