So fassen Sie ein Array von Ganzzahlen in C # zusammen

108

Gibt es einen besseren kürzeren Weg als das Iterieren über das Array?

int[] arr = new int[] { 1, 2, 3 };
int sum = 0;
for (int i = 0; i < arr.Length; i++)
{
    sum += arr[i];
}

Klärung:

Besser primär bedeutet saubereren Code, aber auch Hinweise zur Leistungsverbesserung sind willkommen. (Wie bereits erwähnt: Aufteilen großer Arrays).


Es ist nicht so, dass ich nach einer Verbesserung der Killerleistung gesucht hätte - ich habe mich nur gefragt, ob diese Art von syntaktischem Zucker noch nicht verfügbar ist: "Es gibt String.Join - was zum Teufel ist mit int []?".

Filburt
quelle
2
Besser auf welche Weise? Schneller? Weniger geschriebener Code?
Fredrik Mörk

Antworten:

186

Versuchen Sie es, vorausgesetzt, Sie können .NET 3.5 (oder neuer) und LINQ verwenden

int sum = arr.Sum();
Tomas Vana
quelle
10
Die Identität Lambda ist nicht erforderlich. Außer den neuen Mann im Team zu verwirren.
12
Es ist erwähnenswert, dass dies einen Fehler auslöst, System.OverflowExceptionwenn das Ergebnis größer ist, als Sie in eine vorzeichenbehaftete 32-Bit-Ganzzahl (dh (2 ^ 31) -1 oder in Englisch ~ 2,1 Milliarden) passen können.
ChrisProsser
2
int sum = arr.AsParallel().Sum();Eine schnellere Version, die mehrere Kerne der CPU verwendet. Um zu vermeiden, dass System.OverflowExceptionSie verwenden können long sum = arr.AsParallel().Sum(x => (long)x);Für noch schnellere Versionen, die Überlaufausnahmen vermeiden und alle ganzzahligen Datentypen unterstützen und datenparallele SIMD / SSE-Anweisungen verwenden, werfen Sie einen Blick auf das HPCsharp-Nuget-Paket
DragonSpit
66

Ja da ist. Mit .NET 3.5:

int sum = arr.Sum();
Console.WriteLine(sum);

Wenn Sie .NET 3.5 nicht verwenden, können Sie Folgendes tun:

int sum = 0;
Array.ForEach(arr, delegate(int i) { sum += i; });
Console.WriteLine(sum);
Ahmad Mageed
quelle
2
Warum so eine verschlungene Version vor 3.5? Die foreachSchleife ist in allen Versionen von C # verfügbar.
Jørn Schou-Rode
2
@ Jørn: Das OP bat um einen kürzeren Ansatz. A ersetzt foreachnur eine Codezeile durch eine andere und ist nicht kürzer. Abgesehen davon foreachist a vollkommen in Ordnung und besser lesbar.
Ahmad Mageed
2
Punkt genommen. Das Folgende spart jedoch 18 Zeichen im Vergleich zu Ihrer Stichprobe:foreach (int i in arr) sum += i;
Jørn Schou-Rode
20

Mit LINQ:

arr.Sum()
Chris
quelle
5

Es hängt davon ab, wie Sie besser definieren. Wenn der Code sauberer aussehen soll, können Sie .Sum () verwenden, wie in anderen Antworten erwähnt. Wenn der Vorgang schnell ausgeführt werden soll und Sie über ein großes Array verfügen, können Sie ihn parallel schalten, indem Sie ihn in Untersummen aufteilen und dann die Ergebnisse summieren.

unholysampler
quelle
+1 Sehr guter Punkt zur Leistungsverbesserung, aber ehrlich gesagt war mein ursprünglicher Wunsch, die Iteration loszuwerden.
Filburt
(Niemand sagt Fil, dass er die Iteration nur ein paar Level nach unten geschoben hat)
@ Will: Alter - erwarte nicht, dass ich glaube, wenn ich den Code nicht schreibe, wird Magie passieren ;-)
Filburt
3
Ich vermute, es müsste ein SEHR großes Array sein, bevor eine solche parallele Optimierung Sinn macht.
Ian Mercer
Ja, seit wann ist eine for-Schleife zu einer schlechten Übung geworden?
Ed S.
3

Eine Alternative ist auch die Verwendung der Aggregate()Erweiterungsmethode.

var sum = arr.Aggregate((temp, x) => temp+x);
John Alexiou
quelle
1
Dies scheint dort zu funktionieren, wo Sum es nicht tut. Es würde aus irgendeinem Grund nicht auf einer Reihe von Uint funktionieren, aber Aggregate würde es tun.
John Ernest
2

Wenn Sie LINQ nicht bevorzugen, ist es besser, foreach-Schleife zu verwenden, um einen Indexverlust zu vermeiden.

int[] arr = new int[] { 1, 2, 3 };
int sum = 0;
foreach (var item in arr)
{
   sum += item;
}
HENG Vongkol
quelle
2

Bei extrem großen Arrays kann es sich lohnen, die Berechnung mit mehr als einem Prozessor / Kern der Maschine durchzuführen.

long sum = 0;
var options = new ParallelOptions()
    { MaxDegreeOfParallelism = Environment.ProcessorCount };
Parallel.ForEach(Partitioner.Create(0, arr.Length), options, range =>
{
    long localSum = 0;
    for (int i = range.Item1; i < range.Item2; i++)
    {
        localSum += arr[i];
    }
    Interlocked.Add(ref sum, localSum);
});
Theodor Zoulias
quelle
2

Ein Problem bei den obigen for-Schleifenlösungen besteht darin, dass für das folgende Eingabearray mit allen positiven Werten das Summenergebnis negativ ist:

int[] arr = new int[] { Int32.MaxValue, 1 };
int sum = 0;
for (int i = 0; i < arr.Length; i++)
{
    sum += arr[i];
}
Console.WriteLine(sum);

Die Summe ist -2147483648, da das positive Ergebnis für den Datentyp int zu groß ist und in einen negativen Wert überläuft.

Für dasselbe Eingabearray führen die Vorschläge von arr.Sum () dazu, dass eine Überlaufausnahme ausgelöst wird.

Eine robustere Lösung besteht darin, einen größeren Datentyp, wie in diesem Fall einen "langen", für die "Summe" wie folgt zu verwenden:

int[] arr = new int[] { Int32.MaxValue, 1 };
long sum = 0;
for (int i = 0; i < arr.Length; i++)
{
    sum += arr[i];
}

Die gleiche Verbesserung gilt für die Summierung anderer ganzzahliger Datentypen wie Short und Sbyte. Bei Arrays mit vorzeichenlosen ganzzahligen Datentypen wie uint, ushort und byte wird durch die Verwendung eines vorzeichenlosen Long (ulong) für die Summe die Überlaufausnahme vermieden.

Die for-Schleifenlösung ist auch um ein Vielfaches schneller als Linq .Sum ()

Um noch schneller zu laufen, implementiert das HPCsharp-Nuget-Paket alle diese .Sum () -Versionen sowie SIMD / SSE-Versionen und parallele Multi-Core-Versionen für eine um ein Vielfaches schnellere Leistung.

DragonSpit
quelle
Großartige Idee. Und für ein vorzeichenloses Integer-Array wäre es schön, ulong sum = arr.Sum (x => (ulong) x) ausführen zu können; Leider unterstützt Linq .Sum () keine vorzeichenlosen ganzzahligen Datentypen. Wenn eine vorzeichenlose Summierung erforderlich ist, unterstützt das HPCsharp-Nuget-Paket diese für alle vorzeichenlosen Datentypen.
DragonSpit
Ein Mitwirkender hat eine nette Idee zurückgezogen, long sum = arr.Sum(x => (long)x);die in C # mit Linq gut funktioniert. Es bietet die volle Genauigkeit für die Summierung für alle vorzeichenbehafteten ganzzahligen Datentypen: sbyte, short und int. Es vermeidet auch das Auslösen einer Überlaufausnahme und ist schön kompakt. Es ist nicht so leistungsstark wie die obige for-Schleife, aber die Leistung wird nicht in allen Fällen benötigt.
DragonSpit
0

Die Verwendung von foreach wäre kürzerer Code, führt jedoch zur Laufzeit wahrscheinlich genau dieselben Schritte aus, nachdem die JIT-Optimierung den Vergleich mit Length im for-loop-Steuerungsausdruck erkannt hat.

Ben Voigt
quelle
0

In einer meiner Apps habe ich verwendet:

public class ClassBlock
{
    public int[] p;
    public int Sum
    {
        get { int s = 0;  Array.ForEach(p, delegate (int i) { s += i; }); return s; }
    }
}
Merrais
quelle
Dies entspricht der Verwendung der .Aggregate()Erweiterungsmethode.
John Alexiou
-1

Eine Verbesserung gegenüber Theodor Zoulias 'netter Multi-Core-Parallel.ForEach-Implementierung:

    public static ulong SumToUlongPar(this uint[] arrayToSum, int startIndex, int length, int degreeOfParallelism = 0)
    {
        var concurrentSums = new ConcurrentBag<ulong>();

        int maxDegreeOfPar = degreeOfParallelism <= 0 ? Environment.ProcessorCount : degreeOfParallelism;
        var options = new ParallelOptions() { MaxDegreeOfParallelism = maxDegreeOfPar };

        Parallel.ForEach(Partitioner.Create(startIndex, startIndex + length), options, range =>
        {
            ulong localSum = 0;
            for (int i = range.Item1; i < range.Item2; i++)
                localSum += arrayToSum[i];
            concurrentSums.Add(localSum);
        });

        ulong sum = 0;
        var sumsArray = concurrentSums.ToArray();
        for (int i = 0; i < sumsArray.Length; i++)
            sum += sumsArray[i];

        return sum;
    }

Dies funktioniert für vorzeichenlose ganzzahlige Datentypen, da C # nur Interlocked.Add () für int und long unterstützt. Die obige Implementierung kann auch leicht modifiziert werden, um andere Ganzzahl- und Gleitkomma-Datentypen zu unterstützen, um eine Summierung parallel unter Verwendung mehrerer Kerne der CPU durchzuführen. Es wird im HPCsharp-Nuget-Paket verwendet.

DragonSpit
quelle
-7

Versuchen Sie diesen Code:

using System;

namespace Array
{
    class Program
    {
        static void Main()
        {
            int[] number = new int[] {5, 5, 6, 7};

            int sum = 0;
            for (int i = 0; i <number.Length; i++)
            {
                sum += number[i];
            }
            Console.WriteLine(sum);
        }
    }
} 

Das Ergebnis ist:

23

Ibne Nahian
quelle
Ihr Code ist nicht korrekt. Sie müssen Console.WriteLine (sum) ersetzen. mit Rückgabesumme; und es wird funktionieren
Abdessamad Jadid