Cara meringkas array bilangan bulat di C #

108

Apakah ada cara yang lebih pendek yang lebih baik daripada melakukan iterasi pada array?

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

klarifikasi:

Primer yang lebih baik berarti kode lebih bersih tetapi petunjuk tentang peningkatan kinerja juga diterima. (Seperti yang telah disebutkan: memisahkan array besar).


Ini tidak seperti saya mencari peningkatan kinerja yang mematikan - Saya hanya bertanya-tanya apakah jenis gula sintaksis ini belum tersedia: "Ada String.Join - apa sih int []?".

Filburt
sumber
2
Lebih baik dalam hal apa? Lebih cepat? Kode kurang tertulis?
Fredrik Mörk

Jawaban:

186

Asalkan Anda dapat menggunakan .NET 3.5 (atau lebih baru) dan LINQ, coba

int sum = arr.Sum();
Tomas Vana
sumber
10
Lambda identitas tidak diperlukan. Kecuali untuk membingungkan orang baru di tim.
12
Perlu dicatat bahwa ini akan menimbulkan kesalahan System.OverflowExceptionjika hasilnya lebih besar dari yang dapat Anda masukkan dalam bilangan bulat 32 bit bertanda (yaitu (2 ^ 31) -1 atau dalam bahasa Inggris ~ 2,1 miliar).
ChrisProsser
2
int sum = arr.AsParallel().Sum();versi yang lebih cepat yang menggunakan banyak inti CPU. Untuk menghindari System.OverflowExceptionAnda dapat menggunakan long sum = arr.AsParallel().Sum(x => (long)x);Untuk versi yang lebih cepat yang menghindari pengecualian overflow dan mendukung semua tipe data integer dan menggunakan instruksi SIMD / SSE data paralel, lihat paket nuget HPCsharp
DragonSpit
66

Ya ada. Dengan .NET 3.5:

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

Jika Anda tidak menggunakan .NET 3.5 Anda dapat melakukan ini:

int sum = 0;
Array.ForEach(arr, delegate(int i) { sum += i; });
Console.WriteLine(sum);
Ahmad Mageed
sumber
2
Mengapa versi 3,5 pra yang berbelit-belit? The foreachloop tersedia dalam semua versi C #.
Jørn Schou-Rode
2
@ Jørn: OP meminta pendekatan yang lebih pendek. A foreachhanya menggantikan satu baris kode dengan yang lain dan tidak lebih pendek. Selain itu, a foreachsangat bagus dan lebih mudah dibaca.
Ahmad Mageed
2
Poin diambil. Namun, berikut ini menyimpan 18 karakter dibandingkan dengan sampel Anda:foreach (int i in arr) sum += i;
Jørn Schou-Rode
20

Dengan LINQ:

arr.Sum()
Chris
sumber
5

Itu tergantung pada bagaimana Anda mendefinisikan dengan lebih baik. Jika Anda ingin kode terlihat lebih rapi, Anda dapat menggunakan .Sum () seperti yang disebutkan dalam jawaban lain. Jika Anda ingin operasi berjalan cepat dan Anda memiliki larik yang besar, Anda dapat membuatnya paralel dengan memecahnya menjadi sub-penjumlahan, lalu menjumlahkan hasilnya.

unholysampler
sumber
+1 Poin yang sangat bagus untuk peningkatan kinerja tetapi sejujurnya keinginan awal saya adalah menyingkirkan iterasi.
Filburt
(tidak ada yang memberi tahu Fil bahwa dia baru saja mendorong iterasi beberapa level ke bawah tumpukan)
@ Akan: Bung - jangan berharap saya percaya jika saya tidak menulis kode keajaiban akan terjadi ;-)
Filburt
3
Saya menduga itu harus menjadi array yang SANGAT besar sebelum pengoptimalan paralel seperti itu masuk akal.
Ian Mercer
Ya, sejak kapan loop for menjadi praktik yang buruk?
Ed S.
3

Alternatif juga untuk menggunakan Aggregate()metode ekstensi.

var sum = arr.Aggregate((temp, x) => temp+x);
John Alexiou
sumber
1
Ini tampaknya berhasil di mana Sum tidak. Ini tidak akan berfungsi pada array uint karena alasan tertentu tetapi Agregat akan melakukannya.
John Ernest
2

Jika Anda tidak menyukai LINQ, lebih baik menggunakan foreach loop untuk menghindari keluarnya indeks.

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

Untuk larik yang sangat besar, mungkin ada gunanya melakukan kalkulasi menggunakan lebih dari satu prosesor / inti mesin.

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

Satu masalah dengan solusi loop for di atas adalah untuk array input berikut dengan semua nilai positif, hasil penjumlahannya negatif:

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

Jumlahnya adalah -2147483648, karena hasil positif terlalu besar untuk tipe data int dan meluap ke nilai negatif.

Untuk larik input yang sama, saran arr.Sum () menyebabkan pengecualian overflow dilemparkan.

Solusi yang lebih tepat adalah dengan menggunakan tipe data yang lebih besar, seperti "long" dalam kasus ini, untuk "sum" sebagai berikut:

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

Perbaikan yang sama berfungsi untuk penjumlahan tipe data integer lainnya, seperti short, dan sbyte. Untuk array tipe data integer unsigned seperti uint, ushort dan byte, menggunakan unsigned long (ulong) untuk jumlah tersebut menghindari pengecualian overflow.

Solusi for loop juga beberapa kali lebih cepat dari Linq .Sum ()

Untuk berjalan lebih cepat, paket nuget HPCsharp mengimplementasikan semua versi .Sum () serta versi SIMD / SSE dan paralel multi-core, untuk kinerja yang berkali-kali lebih cepat.

DragonSpit
sumber
Ide yang hebat. Dan, untuk array integer unsigned alangkah baiknya bisa melakukan ulong sum = arr.Sum (x => (ulong) x); Namun, sayangnya, Linq .Sum () tidak mendukung tipe data integer yang tidak ditandatangani. Jika penjumlahan unsigned diperlukan, paket nuget HPCsharp mendukungnya untuk semua tipe data unsigned.
DragonSpit
Seorang kontributor menarik ide bagus long sum = arr.Sum(x => (long)x);yang bekerja dengan baik di C # menggunakan Linq. Ini memberikan akurasi penuh untuk penjumlahan untuk semua tipe data integer yang ditandatangani: sbyte, short, dan int. Ini juga menghindari pelepasan pengecualian luapan, dan kompak dengan baik. Performanya tidak setinggi for loop di atas, tetapi performa tidak diperlukan di semua kasus.
DragonSpit
0

Menggunakan foreach akan menjadi kode yang lebih pendek, tetapi mungkin melakukan langkah yang persis sama pada waktu proses setelah pengoptimalan JIT mengenali perbandingan dengan Panjang dalam ekspresi pengontrol loop-for.

Ben Voigt
sumber
0

Di salah satu aplikasi saya, saya menggunakan:

public class ClassBlock
{
    public int[] p;
    public int Sum
    {
        get { int s = 0;  Array.ForEach(p, delegate (int i) { s += i; }); return s; }
    }
}
merrais
sumber
Ini sama dengan menggunakan .Aggregate()metode ekstensi.
John Alexiou
-1

Peningkatan pada implementasi Parallel.ForEach multi-core yang bagus dari 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;
    }

yang berfungsi untuk tipe data integer unsigned, karena C # hanya mendukung Interlocked.Add () untuk int dan long. Implementasi di atas juga dapat dengan mudah dimodifikasi untuk mendukung tipe data integer dan floating-point lainnya untuk melakukan penjumlahan secara paralel menggunakan beberapa inti CPU. Ini digunakan dalam paket nuget HPCsharp.

DragonSpit
sumber
-7

Coba kode ini:

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

Hasilnya adalah:

23

Ibne Nahian
sumber
Kode Anda tidak Benar, Anda harus mengganti Console.WriteLine (jumlah); dengan jumlah pengembalian; dan itu akan berhasil
Abdessamad Jadid