Як підбити підсумок масиву цілих чисел у 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
Краще в який спосіб? Швидше? Менш написаний код?
Фредрік Мьорк

Відповіді:


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, подивіться на пакунок нульових пакетів
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 #.
Jørn Schou-Rode

2
@ Jørn: ОП попросило коротший підхід. foreachПросто замінює один рядок коду для іншого і не коротше. Крім того, що A foreachідеально чудово і читабельніше.
Ахмад Магід

2
Точка взята. Тим не менше, наступне економить 18 символів порівняно з вашим зразком:foreach (int i in arr) sum += i;
Jørn Schou-Rode


5

Це залежить від того, як ти краще визначишся. Якщо ви хочете, щоб код виглядав чистіше, ви можете використовувати .Sum (), як зазначено в інших відповідях. Якщо ви хочете, щоб операція запускалася швидко, і у вас є великий масив, ви можете зробити її паралельною, розбивши її на підсумки, а потім підсумуйте результати.


+1 Дуже хороший момент щодо підвищення продуктивності, але, чесно кажучи, моє первинне бажання було позбутися від ітерації.
Фільбурт

(ніхто не каже Філу, що він просто штовхнув ітерацію на кілька рівнів вниз по стеку)

@Will: Чувак - не чекай, що я повірю, якщо я не напишу код, це станеться магія ;-)
Filburt

3
Я підозрюю, що перед тим, як паралельна оптимізація матиме сенс, це повинен бути ДУЖЕ великий масив.
Ян Мерсер

Так, з тих пір, коли цикл for for став поганою практикою?
Ред С.

3

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

var sum = arr.Aggregate((temp, x) => temp+x);

1
Це, здається, працює там, де Суми немає. Він не працюватиме з масивом 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];
}

Це ж вдосконалення працює для підсумовування інших цілих типів даних, таких як короткий та sbyte. Для масивів непідписаних цілих типів даних, таких як uint, ushort та byte, використовуючи неподписаний довгий (ulong) для суми, уникає виключення переповнення.

Рішення for циклу також у багато разів швидше, ніж Linq .Sum ()

Щоб запустити ще швидше, пакет нугетів HPCsharp реалізує всі ці версії .Sum (), а також SIMD / SSE версії та багатоядерні паралельні, у багато разів більшу продуктивність.


Чудова ідея. І для безпідписаного цілочислового масиву було б непогано мати змогу зробити улонг суму = arr.Sum (x => (ulong) x); Але, на жаль, Linq .Sum () не підтримує цілі типи даних без підпису. Якщо потрібна безпідписана сумація, пакет нугетів HPCsharp підтримує її для всіх неподписаних типів даних.
DragonSpit

Довідник відкликав приємну ідею, long sum = arr.Sum(x => (long)x);яка чудово працює в C # за допомогою Linq. Він забезпечує повну точність підсумовування для всіх підписаних цілих типів даних: sbyte, short та int. Це також дозволяє уникнути викиду виключення з переповненням і є досить компактним. Це не настільки висока продуктивність, як цикл для циклу вище, але продуктивність потрібна не у всіх випадках.
DragonSpit

0

Використання foreach було б коротшим кодом, але, ймовірно, робити точно такі ж кроки під час виконання після того, як оптимізація JIT розпізнає порівняння з Length у виразі, що контролює 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()метод розширення.
Джон Алексій

-1

Вдосконалення щодо приємної багатоядерної паралельної програми Teodor 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;
    }

який працює для непідписаних цілих типів даних, оскільки C # підтримує лише Interlocked.Add () для int та long. Вищевказана реалізація також може бути легко модифікована для підтримки інших типів даних з цілими числами і плаваючою комою, щоб робити підсумовування паралельно з використанням декількох ядер ЦП. Він використовується в пакеті нугетів 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 (сума); із сумою повернення; і це спрацює
Абдессамад Джадід
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.