Comment résumer un tableau d'entiers en C #

108

Existe-t-il un meilleur moyen plus court que d'itérer sur le tableau?

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

clarification:

Un meilleur primaire signifie un code plus propre, mais des conseils sur l'amélioration des performances sont également les bienvenus. (Comme déjà mentionné: division de grands tableaux).


Ce n'est pas comme si je cherchais une amélioration des performances de tueur - je me demandais juste si ce type de sucre syntaxique n'était pas déjà disponible: "Il y a String.Join - qu'est-ce que diable à propos de int []?".

Filburt
la source
2
Mieux en quoi? Plus rapide? Moins de code écrit?
Fredrik Mörk

Réponses:

186

À condition que vous puissiez utiliser .NET 3.5 (ou plus récent) et LINQ, essayez

int sum = arr.Sum();
Tomas Vana
la source
10
L'identité lambda n'est pas nécessaire. Sauf pour confondre le nouveau gars de l'équipe.
12
Il est à noter que cela provoquera une erreur System.OverflowExceptionsi le résultat est supérieur à ce que vous pouvez insérer dans un entier 32 bits signé (c'est-à-dire (2 ^ 31) -1 ou en anglais ~ 2,1 milliards).
ChrisProsser
2
int sum = arr.AsParallel().Sum();une version plus rapide qui utilise plusieurs cœurs du processeur. Pour éviter que System.OverflowExceptionvous puissiez utiliser long sum = arr.AsParallel().Sum(x => (long)x);Pour des versions encore plus rapides qui évitent les exceptions de débordement et prennent en charge tous les types de données entiers et utilisent des instructions SIMD / SSE parallèles aux données, jetez un œil au package HPCsharp nuget
DragonSpit
66

Oui il y a. Avec .NET 3.5:

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

Si vous n'utilisez pas .NET 3.5, vous pouvez le faire:

int sum = 0;
Array.ForEach(arr, delegate(int i) { sum += i; });
Console.WriteLine(sum);
Ahmad Mageed
la source
2
Pourquoi une telle version pré-3.5 alambiquée? La foreachboucle est disponible dans toutes les versions de C #.
Jørn Schou-Rode
2
@ Jørn: le PO a demandé une approche plus courte. A foreachremplace simplement une ligne de code par une autre et n'est pas plus courte. En dehors de cela, un foreachest parfaitement bien et est plus lisible.
Ahmad Mageed
2
Point pris. Pourtant, ce qui suit enregistre 18 caractères par rapport à votre échantillon:foreach (int i in arr) sum += i;
Jørn Schou-Rode
5

Cela dépend de la façon dont vous définissez mieux. Si vous voulez que le code paraisse plus propre, vous pouvez utiliser .Sum () comme mentionné dans d'autres réponses. Si vous voulez que l'opération s'exécute rapidement et que vous avez un grand tableau, vous pouvez le rendre parallèle en le divisant en sous-sommes, puis en additionnant les résultats.

non-échantillonneur
la source
+1 Très bon point sur l'amélioration des performances, mais honnêtement, mon souhait initial était de me débarrasser de l'itération.
Filburt
(personne ne dit à Fil qu'il vient de pousser l'itération de quelques niveaux dans la pile)
@Will: Mec - ne vous attendez pas à ce que je crois que si je n'écris pas le code, la magie se produira ;-)
Filburt
3
Je soupçonne qu'il faudrait un TRÈS grand tableau avant qu'une telle optimisation parallèle ait un sens.
Ian Mercer
Ouais, depuis quand une boucle for est-elle devenue une mauvaise pratique?
Ed S.
3

Une alternative aussi d'utiliser la Aggregate()méthode d'extension.

var sum = arr.Aggregate((temp, x) => temp+x);
John Alexiou
la source
1
Cela semble fonctionner là où Sum ne fonctionne pas. Cela ne fonctionnerait pas sur un tableau d'uint pour une raison quelconque, mais Aggregate le ferait.
John Ernest
2

Si vous ne préférez pas LINQ, il est préférable d'utiliser la boucle foreach pour éviter la sortie d'index.

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

Pour les tableaux extrêmement volumineux, il peut être utile d'effectuer le calcul en utilisant plusieurs processeurs / cœurs de la machine.

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
la source
2

Un problème avec les solutions de boucle for ci-dessus est que pour le tableau d'entrée suivant avec toutes les valeurs positives, le résultat de la somme est négatif:

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

La somme est -2147483648, car le résultat positif est trop grand pour le type de données int et déborde en une valeur négative.

Pour le même tableau d'entrée, les suggestions arr.Sum () provoquent la levée d'une exception de débordement.

Une solution plus robuste consiste à utiliser un type de données plus grand, tel qu'un "long" dans ce cas, pour la "somme" comme suit:

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

La même amélioration fonctionne pour la sommation d'autres types de données entiers, tels que short et sbyte. Pour les tableaux de types de données entiers non signés tels que uint, ushort et byte, l'utilisation d'un unsigned long (ulong) pour la somme évite l'exception de dépassement de capacité.

La solution de la boucle for est également plusieurs fois plus rapide que Linq .Sum ()

Pour fonctionner encore plus vite, le package HPCsharp nuget implémente toutes ces versions .Sum () ainsi que les versions SIMD / SSE et les versions parallèles multicœurs, pour des performances beaucoup plus rapides.

DragonSpit
la source
Bonne idée. Et, pour un tableau d'entiers non signés, ce serait bien de pouvoir faire ulong sum = arr.Sum (x => (ulong) x); Mais, malheureusement, Linq .Sum () ne prend pas en charge les types de données entiers non signés. Si une sommation non signée est nécessaire, le package HPCsharp nuget la prend en charge pour tous les types de données non signés.
DragonSpit
Un contributeur a retiré une bonne idée de long sum = arr.Sum(x => (long)x);ce qui fonctionne bien en C # en utilisant Linq. Il fournit la précision totale de la sommation pour tous les types de données entiers signés: sbyte, short et int. Cela évite également de lancer une exception de débordement et est bien compact. Ce n'est pas aussi performant que la boucle for ci-dessus, mais les performances ne sont pas nécessaires dans tous les cas.
DragonSpit
0

L'utilisation de foreach serait un code plus court, mais effectuez probablement exactement les mêmes étapes au moment de l'exécution après que l'optimisation JIT ait reconnu la comparaison à Length dans l'expression de contrôle de la boucle for.

Ben Voigt
la source
0

Dans l'une de mes applications, j'ai utilisé:

public class ClassBlock
{
    public int[] p;
    public int Sum
    {
        get { int s = 0;  Array.ForEach(p, delegate (int i) { s += i; }); return s; }
    }
}
merrais
la source
Cela revient à utiliser la .Aggregate()méthode d'extension.
John Alexiou
-1

Une amélioration de l'implémentation multi-core Parallel de Theodor Zoulias.

    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;
    }

qui fonctionne pour les types de données entiers non signés, puisque C # ne prend en charge que Interlocked.Add () pour int et long. L'implémentation ci-dessus peut également être facilement modifiée pour prendre en charge d'autres types de données entiers et flottants pour effectuer une sommation en parallèle à l'aide de plusieurs cœurs du processeur. Il est utilisé dans le package HPCsharp nuget.

DragonSpit
la source
-7

Essayez ce 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);
        }
    }
} 

Le résultat est:

23

Ibne Nahian
la source
Votre code n'est pas correct, vous devez remplacer Console.WriteLine (somme); avec retour somme; et ça marchera
Abdessamad Jadid