Ich habe 3 sehr große vorzeichenbehaftete Ganzzahlen.
long x = long.MaxValue;
long y = long.MaxValue - 1;
long z = long.MaxValue - 2;
Ich möchte ihren abgeschnittenen Durchschnitt berechnen. Der erwartete Durchschnittswert ist long.MaxValue - 1
, das heißt 9223372036854775806
.
Es ist unmöglich, es zu berechnen als:
long avg = (x + y + z) / 3; // 3074457345618258600
Hinweis: Ich habe all diese Fragen zum Durchschnitt von 2 Zahlen gelesen, sehe aber nicht, wie diese Technik auf den Durchschnitt von 3 Zahlen angewendet werden kann.
Es wäre sehr einfach mit der Verwendung von BigInteger
, aber nehmen wir an, ich kann es nicht verwenden.
BigInteger bx = new BigInteger(x);
BigInteger by = new BigInteger(y);
BigInteger bz = new BigInteger(z);
BigInteger bavg = (bx + by + bz) / 3; // 9223372036854775806
Wenn ich konvertiere double
, verliere ich natürlich die Präzision:
double dx = x;
double dy = y;
double dz = z;
double davg = (dx + dy + dz) / 3; // 9223372036854780000
Wenn ich zu konvertiere decimal
, funktioniert es, aber nehmen wir auch an, dass ich es nicht verwenden kann.
decimal mx = x;
decimal my = y;
decimal mz = z;
decimal mavg = (mx + my + mz) / 3; // 9223372036854775806
Frage: Gibt es eine Möglichkeit, den abgeschnittenen Durchschnitt von 3 sehr großen Ganzzahlen nur unter Verwendung des long
Typs zu berechnen ? Betrachten Sie diese Frage nicht als C # -spezifisch, nur ist es für mich einfacher, Beispiele in C # bereitzustellen.
quelle
long.MinValue
undlong.MaxValue
unter Werten habe.BigInteger
oderdecimal
ausgeschlossen ist , oder ist es nur um diese schwer zu machen?Antworten:
Dieser Code wird funktionieren, ist aber nicht so hübsch.
Es teilt zuerst alle drei Werte (es legt die Werte fest, sodass Sie den Rest "verlieren") und teilt dann den Rest:
Beachten Sie, dass das obige Beispiel bei einem oder mehreren negativen Werten nicht immer ordnungsgemäß funktioniert.
Wie mit Ulugbek besprochen, ist hier die aktuelle BEST-Lösung für positive und negative Werte, da die Anzahl der Kommentare unten explodiert.
Dank der Antworten und Kommentare von Ulugbek Umirov , James S. , Kevin Z. , Marc van Leeuwen , gnasher729 ist dies die aktuelle Lösung:
quelle
(x + y + z) / 3 = x / 3 + y / 3 + z / 3
.f(1,1,2) == 1
währendf(-2,-2,8) == 2
(x+y)/3
was zu viel ist.NB - Patrick hat bereits eine großartige Antwort gegeben . Wenn Sie dies erweitern, können Sie eine generische Version für eine beliebige Anzahl von Ganzzahlen erstellen:
quelle
long
, aber bei kleineren Typen ist zu beachten, dass die zweite Summe überlaufen kann.Patrick Hofman hat eine großartige Lösung veröffentlicht . Bei Bedarf kann es jedoch auf verschiedene andere Arten implementiert werden. Mit dem Algorithmus hier habe ich eine andere Lösung. Bei sorgfältiger Implementierung ist es möglicherweise schneller als die Mehrfachaufteilung in Systemen mit langsamen Hardware-Teilern. Es kann weiter optimiert werden , indem die Technik der Division durch Konstanten aus der Freude des Hackers verwendet wird
In C / C ++ auf 64-Bit-Plattformen ist dies viel einfacher
__int128
quelle
x=y/3
Via annähernx=y>>2; x+=x>>2; x+=x>>4; x+=x>>8; x+=x>>16; x+=x>>32;
. Das Ergebnis liegt sehr nahe bei x und kann durch Berechnungdelta=y-x-x-x;
und Anpassungx
nach Bedarf präzisiert werden .Sie können den Mittelwert von Zahlen basierend auf den Unterschieden zwischen den Zahlen berechnen, anstatt die Summe zu verwenden.
Angenommen, x ist das Maximum, y ist der Median, z ist das Min (wie Sie). Wir werden sie max, median und min nennen.
Bedingter Prüfer hinzugefügt gemäß @ UlugbekUmirovs Kommentar:
quelle
(double)(2 / 3)
gleich 0.0?Da C anstelle der euklidischen Division eine Bodenteilung verwendet, ist es möglicherweise einfacher, einen richtig gerundeten Durchschnitt aus drei vorzeichenlosen Werten als drei vorzeichenbehafteten Werten zu berechnen. Fügen Sie einfach 0x8000000000000000UL zu jeder Zahl hinzu, bevor Sie den vorzeichenlosen Durchschnitt nehmen, subtrahieren Sie ihn nach der Aufnahme des Ergebnisses und verwenden Sie einen ungeprüften Cast zurück
Int64
, um einen vorzeichenbehafteten Durchschnitt zu erhalten.Um den vorzeichenlosen Durchschnitt zu berechnen, berechnen Sie die Summe der obersten 32 Bits der drei Werte. Berechnen Sie dann die Summe der unteren 32 Bits der drei Werte plus die Summe von oben plus eins [das Plus eins ergibt ein gerundetes Ergebnis]. Der Durchschnitt beträgt 0x55555555 mal die erste Summe plus ein Drittel der zweiten.
Die Leistung auf 32-Bit-Prozessoren kann verbessert werden, indem drei "Summen" -Werte erzeugt werden, von denen jeder 32 Bit lang ist, so dass das Endergebnis ist
((0x55555555UL * sumX)<<32) + 0x55555555UL * sumH + sumL/3
; es könnte möglicherweise weiter durch den Austausch verstärkt werdensumL/3
mit((sumL * 0x55555556UL) >> 32)
, obwohl letztere auf den JIT - Optimierer abhängen würde [es vielleicht wissen , wie eine Division durch 3 mit einem mehrfach zu ersetzen und seinen Code könnte in der Tat effizienter sein als eine explizite Multiplikationsoperation].quelle
Patching Patrick Hofman ‚s Lösung mit supercat ‘ s Korrektur, gebe ich Ihnen folgendes:
Und der N-Element-Fall:
Dies gibt immer den Boden () des Mittelwerts an und eliminiert jeden möglichen Randfall.
quelle
Sie könnten die Tatsache nutzen, dass Sie jede der Zahlen als schreiben können
y = ax + b
, wobeix
es sich um eine Konstante handelt. Jedera
wärey / x
(der ganzzahlige Teil dieser Division). Jedes b wärey % x
(der Rest / Modulo dieser Division). Wenn Sie diese Konstante auf intelligente Weise auswählen, indem Sie beispielsweise die Quadratwurzel der maximalen Anzahl als Konstante auswählen, können Sie den Durchschnitt von erhaltenx
Zahlen erhalten, ohne Probleme mit dem Überlauf zu haben.Der Durchschnitt einer beliebigen Liste von Zahlen kann wie folgt ermittelt werden:
wobei
%
modulo und/
den "ganzen" Teil der Teilung bezeichnet.Das Programm würde ungefähr so aussehen:
quelle
Wenn Sie wissen, dass Sie N Werte haben, können Sie jeden Wert durch N teilen und summieren?
quelle
Ich habe es auch versucht und eine schnellere Lösung gefunden (allerdings nur um einen Faktor von 3/4). Es wird eine einzelne Abteilung verwendet
Dabei
smallDiv3
wird durch Multiplikation durch 3 dividiert und nur für kleine Argumente gearbeitetHier ist der gesamte Code einschließlich eines Tests und eines Benchmarks, die Ergebnisse sind nicht so beeindruckend.
quelle
Diese Funktion berechnet das Ergebnis in zwei Unterteilungen. Es sollte sich gut auf andere Teiler und Wortgrößen verallgemeinern lassen.
Es funktioniert, indem das Ergebnis der Doppelwortaddition berechnet und dann die Division berechnet wird.
quelle
Mathematik
Code
quelle
{1,2,3}
der Antwort ist2
, aber Ihr Code wird zurückkehren1
.double
, da wir in einem solchen Fall an Präzision verlieren werden.Versuche dies:
quelle