Average function without overflow exception

后端 未结 18 1737
一生所求
一生所求 2021-02-12 14:58

.NET Framework 3.5.
I\'m trying to calculate the average of some pretty large numbers.
For instance:

using System;
using System.Linq;

class Program
{
           


        
相关标签:
18条回答
  • 2021-02-12 15:20

    I guess there has to be a compromise somewhere or the other. If the numbers are really getting so large then few digits of lower orders (say lower 5 digits) might not affect the result as much.

    Another issue is where you don't really know the size of the dataset coming in, especially in stream/real time cases. Here I don't see any solution other then the (previousAverage*oldCount + newValue) / (oldCount <- oldCount+1)


    Here's a suggestion:

    *LargestDataTypePossible* currentAverage;
    *SomeSuitableDatatypeSupportingRationalValues* newValue;
    
    *int* count;
    addToCurrentAverage(value){
     newValue = value/100000;
     count = count + 1;
     currentAverage = (currentAverage * (count-1) + newValue) / count;
    }
    
    getCurrentAverage(){
     return currentAverage * 100000;
    }
    
    0 讨论(0)
  • 2021-02-12 15:20

    How about BigInteger in Visual J#.

    0 讨论(0)
  • 2021-02-12 15:21

    This answer used to suggest storing the quotient and remainder (mod count) separately. That solution is less space-efficient and more code-complex.

    In order to accurately compute the average, you must keep track of the total. There is no way around this, unless you're willing to sacrifice accuracy. You can try to store the total in fancy ways, but ultimately you must be tracking it if the algorithm is correct.

    For single-pass algorithms, this is easy to prove. Suppose you can't reconstruct the total of all preceding items, given the algorithm's entire state after processing those items. But wait, we can simulate the algorithm then receiving a series of 0 items until we finish off the sequence. Then we can multiply the result by the count and get the total. Contradiction. Therefore a single-pass algorithm must be tracking the total in some sense.

    Therefore the simplest correct algorithm will just sum up the items and divide by the count. All you have to do is pick an integer type with enough space to store the total. Using a BigInteger guarantees no issues, so I suggest using that.

    var total = BigInteger.Zero
    var count = 0
    for i in values
        count += 1
        total += i
    return total / (double)count //warning: possible loss of accuracy, maybe return a Rational instead?
    
    0 讨论(0)
  • 2021-02-12 15:28

    NextAverage = CurrentAverage + (NewValue - CurrentAverage) / (CurrentObservations + 1)

    0 讨论(0)
  • 2021-02-12 15:30

    If you're willing to sacrifice precision, you could do something like:

    long num2 = 0L;
    foreach (long num3 in source)
    {
        num2 += 1L;
    }
    if (num2 <= 0L)
    {
        throw Error.NoElements();
    }
    double average = 0;
    foreach (long num3 in source)
    {
        average += (double)num3 / (double)num2;
    }
    return average;
    
    0 讨论(0)
  • 2021-02-12 15:30

    Here is my version of an extension method that can help with this.

        public static long Average(this IEnumerable<long> longs)
        {
            long mean = 0;
            long count = longs.Count();
            foreach (var val in longs)
            {
                mean += val / count;
            }
            return mean;
        }
    
    0 讨论(0)
提交回复
热议问题