Como somar uma matriz de inteiros em C #

108

Existe uma maneira mais curta melhor do que iterar no array?

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

esclarecimento:

Melhor primário significa código mais limpo, mas dicas sobre melhoria de desempenho também são bem-vindas. (Como já mencionado: dividindo grandes arrays).


Não é como se eu estivesse procurando por uma melhoria de desempenho matadora - eu apenas me perguntei se esse tipo de açúcar sintático já não estava disponível: "Há String.Join - que diabos sobre int []?".

Filburt
fonte
2
Melhor de que maneira? Mais rápido? Código menos escrito?
Fredrik Mörk

Respostas:

186

Desde que você possa usar .NET 3.5 (ou mais recente) e LINQ, tente

int sum = arr.Sum();
Tomas Vana
fonte
10
A identidade lambda não é necessária. Exceto para confundir o novo cara da equipe.
12
É importante notar que isso gerará um erro System.OverflowExceptionse o resultado for maior do que você pode caber em um inteiro de 32 bits com sinal (ou seja, (2 ^ 31) -1 ou em inglês ~ 2,1 bilhões).
ChrisProsser
2
int sum = arr.AsParallel().Sum();uma versão mais rápida que usa vários núcleos da CPU. Para evitar, System.OverflowExceptionvocê pode usar long sum = arr.AsParallel().Sum(x => (long)x);Para versões ainda mais rápidas que evitam exceção de estouro e suportam todos os tipos de dados inteiros e usam instruções SIMD / SSE paralelas de dados, dê uma olhada no pacote HPCsharp nuget
DragonSpit
66

Sim existe. Com .NET 3.5:

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

Se você não estiver usando .NET 3.5, poderá fazer o seguinte:

int sum = 0;
Array.ForEach(arr, delegate(int i) { sum += i; });
Console.WriteLine(sum);
Ahmad Mageed
fonte
2
Por que uma versão pré 3.5 tão complicada? O foreachloop está disponível em todas as versões do C #.
Jørn Schou-Rode
2
@ Jørn: o OP pediu uma abordagem mais curta. A foreachapenas substitui uma linha de código por outra e não é mais curta. Além disso, a foreachestá perfeitamente correto e é mais legível.
Ahmad Mageed
2
Ponto tomado. Ainda assim, o seguinte economiza 18 caracteres em comparação com sua amostra:foreach (int i in arr) sum += i;
Jørn Schou-Rode
20

Com LINQ:

arr.Sum()
Chris
fonte
5

Depende de como você define melhor. Se quiser que o código tenha uma aparência mais limpa, você pode usar .Sum () conforme mencionado em outras respostas. Se quiser que a operação seja executada rapidamente e tiver uma grande matriz, você pode torná-la paralela dividindo-a em sub-somas e, em seguida, somar os resultados.

unholysampler
fonte
1 Ponto muito bom sobre melhoria de desempenho, mas honestamente, meu desejo inicial era livrar-me da iteração.
Filburt
(ninguém diz a Fil que ele empurrou a iteração alguns níveis para baixo na pilha)
@ Will: Cara - não espere que eu acredite que se eu não escrever o código, a mágica vai acontecer ;-)
Filburt
3
Eu suspeito que teria que ser um array MUITO grande antes que essa otimização paralela fizesse sentido.
Ian Mercer,
Sim, desde quando um loop for se tornou uma prática ruim?
Ed S.
3

Uma alternativa também é usar o Aggregate()método de extensão.

var sum = arr.Aggregate((temp, x) => temp+x);
John alexiou
fonte
1
Isso parece funcionar onde Sum não funciona. Não funcionaria em um array de uint por algum motivo, mas o Aggregate sim.
John Ernest
2

Se você não preferir o LINQ, é melhor usar o loop foreach para evitar fora do índice.

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

Para matrizes extremamente grandes, pode valer a pena realizar o cálculo usando mais de um processador / núcleos da máquina.

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
fonte
2

Um problema com as soluções de loop for acima é que para a seguinte matriz de entrada com todos os valores positivos, o resultado da soma é negativo:

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

A soma é -2147483648, pois o resultado positivo é muito grande para o tipo de dados int e transborda para um valor negativo.

Para a mesma matriz de entrada, as sugestões arr.Sum () fazem com que uma exceção de estouro seja lançada.

Uma solução mais robusta é usar um tipo de dados maior, como um "longo" neste caso, para a "soma" da seguinte maneira:

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

A mesma melhoria funciona para a soma de outros tipos de dados inteiros, como short e sbyte. Para matrizes de tipos de dados inteiros sem sinal, como uint, ushort e byte, o uso de um longo sem sinal (ulong) para a soma evita a exceção de estouro.

A solução do loop for também é muitas vezes mais rápida que Linq .Sum ()

Para funcionar ainda mais rápido, o pacote HPCsharp nuget implementa todas essas versões .Sum (), bem como versões SIMD / SSE e paralelas com vários núcleos, para desempenho muitas vezes mais rápido.

DragonSpit
fonte
Boa ideia. E, para array inteiro sem sinal, seria bom ser capaz de fazer ulong sum = arr.Sum (x => (ulong) x); Mas, infelizmente, Linq .Sum () não suporta tipos de dados inteiros sem sinal. Se a soma não assinada for necessária, o pacote HPCsharp nuget oferece suporte para todos os tipos de dados não assinados.
DragonSpit de
Um contribuidor retirou uma boa ideia long sum = arr.Sum(x => (long)x);que funciona bem em C # usando Linq. Ele fornece a precisão total para a soma de todos os tipos de dados inteiros com sinal: sbyte, short e int. Ele também evita o lançamento de uma exceção de estouro e é bem compacto. Não é um desempenho tão alto quanto o loop for acima, mas o desempenho não é necessário em todos os casos.
DragonSpit de
0

Usar foreach seria um código mais curto, mas provavelmente executaria exatamente as mesmas etapas em tempo de execução depois que a otimização JIT reconhecer a comparação com Length na expressão de controle do loop for.

Ben Voigt
fonte
0

Em um de meus aplicativos eu usei:

public class ClassBlock
{
    public int[] p;
    public int Sum
    {
        get { int s = 0;  Array.ForEach(p, delegate (int i) { s += i; }); return s; }
    }
}
Merrane
fonte
Isso é o mesmo que usar o .Aggregate()método de extensão.
John Alexiou
-1

Uma melhoria na boa implementação Parallel.ForEach multi-core 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;
    }

que funciona para tipos de dados inteiros sem sinal, uma vez que C # suporta apenas Interlocked.Add () para int e long. A implementação acima também pode ser facilmente modificada para oferecer suporte a outros tipos de dados inteiros e de ponto flutuante para fazer a soma em paralelo usando vários núcleos da CPU. É usado no pacote nuget HPCsharp.

DragonSpit
fonte
-7

Experimente este código:

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

O resultado é:

23

Ibne Nahian
fonte
Seu código não está correto, você deve substituir Console.WriteLine (sum); com soma de retorno; e funcionará
Abdessamad Jadid