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 또는 영어로 ~ 21 억)에 맞출 수있는 것보다 크면 오류가 발생한다는 점에 주목할 가치 가 있습니다.
ChrisProsser

2
int sum = arr.AsParallel().Sum();CPU의 여러 코어를 사용하는 더 빠른 버전입니다. 방지하기 위해 System.OverflowException사용할 수있는 long sum = arr.AsParallel().Sum(x => (long)x);피하기 오버 플로우 예외가 더 빠른 버전 및 지원하는 모든 정수 데이터 유형과 사용 데이터를 SIMD / SSE 명령어를 평행 HPCsharp의 nuget 패키지를 살펴 걸릴
DragonSpit

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 : OP는 더 짧은 접근 방식을 요구했습니다. A foreach는 한 줄의 코드를 다른 줄로 대체하고 짧지 않습니다. 그 외에도 a foreach는 완벽하고 더 읽기 쉽습니다.
Ahmad Mageed

2
요점을 알았어. 그러나 다음은 샘플에 비해 18자를 절약합니다.foreach (int i in arr) sum += i;
Jørn Schou-Rode


5

어떻게 더 잘 정의 하느냐에 달려 있습니다. 코드가 더 깔끔해 보이기를 원한다면 다른 답변에서 언급했듯이 .Sum ()을 사용할 수 있습니다. 작업이 빠르게 실행되기를 원하고 큰 배열이있는 경우 하위 합계로 나누고 결과를 합산하여 병렬로 만들 수 있습니다.


+1 성능 향상에 대한 매우 좋은 점이지만 솔직히 내 초기 소망은 반복을 제거하는 것이 었습니다.
Filburt 2010 년

(아무도 Fil에게 그가 반복을 스택 아래로 몇 단계 밀어

@Will

3
병렬 최적화가 이해되기 전에 매우 큰 배열이어야한다고 생각합니다.
Ian Mercer

예, for 루프가 언제부터 나쁜 습관이 되었습니까?
Ed S.

3

Aggregate()확장 방법 을 사용하는 대안도 있습니다 .

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

1
이것은 Sum이 작동하지 않는 곳에서 작동하는 것 같습니다. 어떤 이유로 uint 배열에서는 작동하지 않지만 Aggregate는 작동합니다.
John Ernest

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

양수 결과가 int 데이터 유형에 비해 너무 커서 음수 값으로 오버플로되기 때문에 합계는 -2147483648입니다.

동일한 입력 배열의 경우 arr.Sum () 제안으로 인해 오버플로 예외가 발생합니다.

보다 강력한 솔루션은 다음과 같이 "합계"에 대해 "long"과 같은 더 큰 데이터 유형을 사용하는 것입니다.

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와 같은 부호없는 정수 데이터 유형의 배열의 경우 합계에 부호없는 long (ulong)을 사용하면 오버 플로우 예외가 방지됩니다.

for 루프 솔루션은 Linq .Sum ()보다 몇 배 더 빠릅니다.

더 빠르게 실행하기 위해 HPCsharp nuget 패키지는 이러한 모든 .Sum () 버전은 물론 SIMD / SSE 버전 및 멀티 코어 병렬 버전을 구현하여 성능을 여러 배 향상시킵니다.


좋은 생각입니다. 그리고 부호없는 정수 배열의 경우 ulong sum = arr.Sum (x => (ulong) x); 그러나 슬프게도 Linq .Sum ()은 부호없는 정수 데이터 유형을 지원하지 않습니다. 서명되지 않은 합계가 필요한 경우 HPCsharp 너겟 패키지는 서명되지 않은 모든 데이터 유형에 대해이를 지원합니다.
DragonSpit

기여자는 long sum = arr.Sum(x => (long)x);Linq를 사용하여 C #에서 잘 작동 하는 좋은 아이디어를 철회했습니다 . 모든 부호있는 정수 데이터 유형 (sbyte, short 및 int)에 대한 합계에 대한 전체 정확도를 제공합니다. 또한 오버플로 예외가 발생하지 않으며 매우 간결합니다. 위의 for 루프만큼 고성능은 아니지만 모든 경우에 성능이 필요한 것은 아닙니다.
DragonSpit 19

0

foreach를 사용하면 코드가 더 짧을 수 있지만 JIT 최적화가 for 루프 제어 표현식에서 Length와의 비교를 인식 한 후 런타임에 정확히 동일한 단계를 수행 할 수 있습니다.


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

Theodor Zoulias의 멋진 멀티 코어 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 #은 int 및 long에 대해 Interlocked.Add () 만 지원하므로 부호없는 정수 데이터 형식에 대해 작동합니다. 위의 구현은 CPU의 다중 코어를 사용하여 병렬로 합산을 수행하기 위해 다른 정수 및 부동 소수점 데이터 유형을 지원하도록 쉽게 수정할 수 있습니다. HPCsharp nuget 패키지에서 사용됩니다.


-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); 반환 합계로; 그것은 작동합니다
Abdessamad 디드에게
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.