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 []?".


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

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

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


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.


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

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

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

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.


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.


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

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.


-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


Seu código não está correto, você deve substituir Console.WriteLine (sum); com soma de retorno; e funcionará
— Abdessamad Jadid
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.