Как суммировать массив целых чисел в C #

108

Есть ли более короткий способ, чем повторение массива?

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

пояснение:

Лучшее первичное означает более чистый код, но также приветствуются намеки на повышение производительности. (Как уже упоминалось: разбиение больших массивов).


Не то чтобы я искал убийственного улучшения производительности - я просто подумал, не существует ли еще такого синтаксического сахара : «Есть String.Join - какого черта насчет int []?».

Филбурт
источник
2
Лучше в чем? Быстрее? Меньше написанного кода?
Fredrik Mörk

Ответы:

186

При условии, что вы можете использовать .NET 3.5 (или новее) и LINQ, попробуйте

int sum = arr.Sum();
Томас Вана
источник
10
Лямбда идентичности не требуется. Разве что сбить с толку нового парня в команде.
12
Стоит отметить, что это вызовет ошибку, System.OverflowExceptionесли результат будет больше, чем вы можете поместить в 32-битное целое число со знаком (т.е. (2 ^ 31) -1 или на английском языке ~ 2,1 миллиарда).
ChrisProsser
2
int sum = arr.AsParallel().Sum();более быстрая версия, использующая несколько ядер ЦП. Чтобы избежать того, что System.OverflowExceptionвы можете использовать long sum = arr.AsParallel().Sum(x => (long)x);еще более быстрые версии, которые избегают исключения переполнения и поддерживают все целочисленные типы данных и используют параллельные инструкции SIMD / SSE для данных, взгляните на пакет
Nuget HPCsharp
66

Да, есть. С .NET 3.5:

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

Если вы не используете .NET 3.5, вы можете сделать это:

int sum = 0;
Array.ForEach(arr, delegate(int i) { sum += i; });
Console.WriteLine(sum);
Ахмад Магид
источник
2
Почему такая запутанная версия до 3.5? foreachЦикл доступен во всех версиях C #.
Йорн Скоу-Роде,
2
@ Jørn: OP попросил более короткий подход. A foreachпросто заменяет одну строку кода другой и не короче. Кроме того, a foreachпрекрасно и более читабельно.
Ахмад Магид,
2
Дело принято. Тем не менее, следующее сохраняет 18 символов по сравнению с вашим образцом:foreach (int i in arr) sum += i;
Йорн Скоу-Роде,
5

Это зависит от того, как вы определитесь лучше. Если вы хотите, чтобы код выглядел чище, вы можете использовать .Sum (), как указано в других ответах. Если вы хотите, чтобы операция выполнялась быстро и у вас есть большой массив, вы можете сделать его параллельным, разбив его на промежуточные суммы, а затем суммируя результаты.

нечестивый
источник
+1 Очень хороший отзыв об улучшении производительности, но, честно говоря, моим первоначальным желанием было избавиться от итерации.
Filburt
(Никто не говорит Филу, что он просто продвинул итерацию на пару уровней вниз по стеку)
@Will: Чувак, не ждите, что я поверю, если я не напишу код, случится волшебство ;-)
Филбурт
3
Я подозреваю, что это должен быть ОЧЕНЬ большой массив, чтобы такая параллельная оптимизация имела смысл.
Ян Мерсер
Да, с каких это пор цикл for стал плохой практикой?
Эд С.
3

Альтернативой также является использование Aggregate()метода расширения.

var sum = arr.Aggregate((temp, x) => temp+x);
Джон Алексиу
источник
1
Кажется, это работает там, где Sum нет. По какой-то причине он не работал бы с массивом uint, но Aggregate работал бы.
Джон Эрнест
2

Если вы не предпочитаете LINQ, лучше использовать цикл foreach, чтобы избежать выхода из индекса.

int[] arr = new int[] { 1, 2, 3 };
int sum = 0;
foreach (var item in arr)
{
   sum += item;
}
ХЕНГ Вонгкол
источник
2

Для очень больших массивов может потребоваться выполнение вычислений с использованием более чем одного процессора / ядра машины.

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);
});
Теодор Зулиас
источник
2

Одна из проблем с решениями цикла for выше заключается в том, что для следующего входного массива со всеми положительными значениями результат суммы отрицательный:

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

Сумма равна -2147483648, поскольку положительный результат слишком велик для типа данных int и принимает отрицательное значение.

Для того же входного массива предложения arr.Sum () вызывают исключение переполнения.

Более надежным решением является использование более крупного типа данных, такого как «длинный» в данном случае, для «суммы» следующим образом:

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

То же улучшение работает для суммирования других целочисленных типов данных, таких как short и sbyte. Для массивов беззнаковых целочисленных типов данных, таких как uint, ushort и byte, использование unsigned long (ulong) для суммы позволяет избежать исключения переполнения.

Решение цикла for также во много раз быстрее, чем Linq .Sum ()

Чтобы работать еще быстрее, пакет HPCsharp nuget реализует все эти версии .Sum (), а также версии SIMD / SSE и многоядерные параллельные версии, что во много раз увеличивает производительность.

Яма Дракона
источник
Отличная идея. И для целочисленного массива без знака было бы неплохо иметь возможность делать ulong sum = arr.Sum (x => (ulong) x); Но, к сожалению, Linq .Sum () не поддерживает целочисленные типы данных без знака. Если требуется суммирование без знака, пакет nuget HPCsharp поддерживает его для всех типов данных без знака.
DragonSpit
Один из участников отказался от хорошей идеи, long sum = arr.Sum(x => (long)x);которая хорошо работает на C # с помощью Linq. Он обеспечивает полную точность суммирования для всех целочисленных типов данных со знаком: sbyte, short и int. Он также позволяет избежать исключения переполнения и очень компактен. Это не такая высокая производительность, как в приведенном выше цикле for, но производительность требуется не во всех случаях.
DragonSpit
0

Использование foreach было бы более коротким кодом, но, вероятно, выполняло бы точно такие же шаги во время выполнения после того, как оптимизация JIT распознает сравнение с длиной в управляющем выражении цикла for.

Бен Фойгт
источник
0

В одном из своих приложений я использовал:

public class ClassBlock
{
    public int[] p;
    public int Sum
    {
        get { int s = 0;  Array.ForEach(p, delegate (int i) { s += i; }); return s; }
    }
}
Меррайс
источник
Это то же самое, что и при использовании .Aggregate()метода расширения.
John Alexiou
-1

Улучшение красивой многоядерной реализации Parallel.ForEach Теодора Зулиаса:

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

который работает для целочисленных типов данных без знака, поскольку C # поддерживает только Interlocked.Add () для int и long. Вышеупомянутая реализация также может быть легко изменена для поддержки других типов данных с целыми числами и с плавающей запятой для параллельного суммирования с использованием нескольких ядер ЦП. Он используется в пакете nuget HPCsharp.

Яма Дракона
источник
-7

Попробуйте этот код:

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

Результат:

23

Ибне Нахиан
источник
Ваш код неверен, необходимо заменить Console.WriteLine (sum); с суммой возврата; и это будет работать
Абдессамад Джадид