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
どのように良いですか?もっと早く?書かれたコードが少ない?
FredrikMörk10年

回答:


186

.NET 3.5(以降)およびLINQを使用できる場合は、

int sum = arr.Sum();

10
IDラムダは必要ありません。チームの新しい男を混乱させることを除いて。

12
System.OverflowException結果が符号付き32ビット整数(つまり、(2 ^ 31)-1または英語〜21億)に収まらない場合、エラーが発生することは注目に値します。
ChrisProsser 2015年

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ørnSchou-Rode、2010

2
@ヨルン:OPはより短いアプローチを求めました。Aは、foreachちょうど別のコードの1行を置換し、短いではありません。それとは別に、a foreachは完全に問題なく、より読みやすくなっています。
Ahmad Mageed 2010年

2
ポイントを取る。ただし、次の例では、サンプルと比較して18文字が節約されます。foreach (int i in arr) sum += i;
JørnSchou-Rode


5

それはあなたがよりよく定義する方法に依存します。コードをきれいに見せたい場合は、他の回答で述べたように.Sum()を使用できます。操作をすばやく実行する必要があり、配列が大きい場合は、小計に分割して結果を合計することで、並列化できます。


+1パフォーマンス向上に関しては非常に良い点ですが、正直なところ、最初の望みはイテレーションを取り除くことでした。
Filburt

(Filにイタレーションをスタックの数レベル下にプッシュしたと誰も言わない)

@Will:おい-コードマジックを書いていないと信じられないと私は期待しないでくださいマジックが起こります;-)
Filburt

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ループソリューションの1つの問題は、次の入力配列がすべて正の値の場合、合計結果が負になることです。

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()の提案により、オーバーフロー例外がスローされます。

より堅牢なソリューションは、次のように、「合計」にこの場合の「長い」などのより大きなデータ型を使用することです。

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()は符号なし整数データ型をサポートしていません。符号なしの合計が必要な場合、HPCsharp nugetパッケージは、すべての符号なしデータ型に対してそれをサポートします。
DragonSpit

貢献者は、long sum = arr.Sum(x => (long)x);Linqを使用してC#でうまく機能するという素晴らしいアイデアを撤回しました。すべての符号付き整数データ型(sbyte、short、int)の合計の完全な精度を提供します。また、オーバーフロー例外のスローを回避し、コンパクトです。上記のforループほど高いパフォーマンスではありませんが、すべてのケースでパフォーマンスが必要なわけではありません。
DragonSpit

0

foreachを使用するとコードは短くなりますが、JIT最適化がforループ制御式のLengthとの比較を認識した後、実行時におそらく同じ手順を実行します。


0

私が使用した私のアプリの1つで:

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#がInterlocked.Add()をintおよびlongに対してのみサポートするため、符号なし整数データ型に対して機能します。上記の実装は、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);を置き換える必要があります。返品合計あり。そしてそれは動作します
アブデッサマドジャディッド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.