19 Stimmen

Gibt es eine Möglichkeit, das arithmetische Mittel "besser" als Summe()/N zu finden?

Angenommen, wir haben N Zahlen (ganze Zahlen, Gleitkommazahlen, was immer Sie wollen) und wollen ihr arithmetisches Mittel finden. Die einfachste Methode ist, alle Werte zu summieren und durch die Anzahl der Werte zu dividieren:

def simple_mean(array[N]): # pseudocode
    sum = 0
    for i = 1 to N
       sum += array[i]
    return sum / N

Es funktioniert gut, erfordert aber große ganze Zahlen. Wenn wir keine großen ganzen Zahlen wollen und mit Rundungsfehlern zurechtkommen und N eine Potenz von zwei ist, können wir "Teilen und Beherrschen" verwenden: ((a+b)/2 + (c+d)/2)/2 = (a+b+c+d)/4 , ((a+b+c+d)/4 + (e+f+g+h)/4)/2 = (a+b+c+d+e+f+g+h)/8 und so weiter.

def bisection_average(array[N]):
   if N == 1: return array[1]
   return (bisection_average(array[:N/2])+bisection_average(array[N/2:]))/2

Gibt es noch andere Möglichkeiten?

PS. Spielplatz für Faule

0 Stimmen

Interessant, aber der Teil über "Rundungsfehler sind kein Problem" hat mich beunruhigt. Ich würde eine Methode bevorzugen, bei der keine Fehler auftreten.

0 Stimmen

Wenn ich es mir recht überlege, werde ich morgen früh darauf zurückkommen und meine Antwort wieder löschen, wenn ich dann immer noch froh bin, dass sie nicht völlig falsch ist...

0 Stimmen

@pavium: Wenn Sie eine fehlerfreie Methode wünschen, müssen Sie diese von Hand berechnen.

30voto

Jason S Punkte 178087

Knuth führt die folgende Methode zur Berechnung von Mittelwert und Standardabweichung bei Fließkommazahlen auf (Original auf S. 232 von Band 2 von Die Kunst der Computerprogrammierung (Ausgabe von 1998; in meiner nachstehenden Bearbeitung wird die erste Fassung nicht besonders hervorgehoben):

double M=0, S=0;

for (int i = 0; i < N; ++i)
{
    double Mprev = M;
    M += (x[i] - M)/(i+1);
    S += (x[i] - M)*(x[i] - Mprev);
}

// mean = M
// std dev = sqrt(S/N) or sqrt(S/N+1)
// depending on whether you want population or sample std dev

0 Stimmen

Sollte nicht S += (x[i] - M)*(x[i] - Mprev); sein S += (x[i] - Mprev)*(x[i] - Mprev); ?

1 Stimmen

Nein. Siehe jonisalonen.com/2013/

0 Stimmen

Stichprobenstandardabweichung, wäre sqrt(S/(N-1))

17voto

sepp2k Punkte 352762

Hier ist eine Möglichkeit, den Mittelwert nur mit ganzen Zahlen zu berechnen, ohne Rundungsfehler und ohne große Zwischenwerte:

sum = 0
rest = 0
for num in numbers:
  sum += num / N
  rest += num % N
  sum += rest / N
  rest = rest % N

return sum, rest

0 Stimmen

Dabei wird grundsätzlich die Multipräzisionsarithmetik (Doppelwort) verwendet. Ich denke, es gibt eine Möglichkeit, dies zu optimieren, um die Anzahl der divide-ähnlichen (/ oder %) Operationen zu reduzieren, aber ich kann mich nicht erinnern, aus dem Kopf.

0 Stimmen

Die übliche Technik besteht darin, X/N und X%N in einer einzigen Funktion/einem einzigen Vorgang zu berechnen. Der Grund dafür ist, dass die zugrunde liegenden Algorithmen ziemlich gleich sind.

0 Stimmen

Ja, obwohl C sie nicht offenlegt. >:( Nein, ich meinte eher: sum += (num + rest) / N; rest = (num + rest) % N; außer dass das anfällig für einen Überlauf sein kann

3voto

Svetlozar Angelov Punkte 20324

Wenn die großen ganzen Zahlen ein Problem sind... ist es in Ordnung

a/N + b/N+.... n/N

Ich meine, Sie suchen nur nach anderen Wegen oder nach dem optimalen Weg?

2 Stimmen

Warum?!?! Wenn a, b, etc. ganze Zahlen sind, erhalten Sie eine falsche Antwort. Wenn es sich um Fließkommazahlen handelt, bin ich mir nicht sicher, aber ich vermute, dass Sie mehr Rundungsfehler erhalten, als wenn Sie nur eine Summe bilden und dann dividieren. In jedem Fall wird die Rechenzeit für einen fragwürdigen Nutzen stark erhöht.

3voto

Stephen Canon Punkte 100340

Handelt es sich bei dem Array um Fließkommadaten, leidet selbst der "einfache" Algorithmus unter Rundungsfehlern. Interessanterweise führt in diesem Fall das Blockieren der Berechnung in sqrt(N)-Summen der Länge sqrt(N) tatsächlich zu einer Verringerung des Fehlers im durchschnittlichen Fall (obwohl die gleiche Anzahl von Fließkommarundungen durchgeführt wird).

Wenn Sie weniger als 4 Milliarden Elemente in Ihrem Array haben (was wahrscheinlich ist), brauchen Sie nur einen Integer-Typ, der 32 Bit größer ist als der Typ der Array-Daten. Die Addition auf diesem etwas größeren Typ wird so gut wie immer schneller sein als die Division oder der Modulus auf dem Typ selbst. Auf den meisten 32-Bit-Systemen ist zum Beispiel die 64-Bit-Addition schneller als die 32-Bit-Division/Modulierung. Dieser Effekt wird nur noch stärker, wenn die Typen größer werden.

1voto

Nick Dandoulakis Punkte 41402

Wenn Sie float können Sie große ganze Zahlen vermeiden:

def simple_mean(array[N]):
    sum = 0.0 # <---
    for i = 1 to N
       sum += array[i]
    return sum / N

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X