Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 当所有值之和超过一个双精度';什么是限制?_Java_Algorithm_Statistics - Fatal编程技术网

Java 当所有值之和超过一个双精度';什么是限制?

Java 当所有值之和超过一个双精度';什么是限制?,java,algorithm,statistics,Java,Algorithm,Statistics,我需要计算一组非常大的双精度(10^9个值)的平均值。这些值的总和超过了双精度的上限,那么有人知道计算平均值的一些巧妙的小技巧,而不需要同时计算总和吗 我正在使用Java1.5 您可以取不超过限制的大小相等的数字子集的平均值 将所有值除以设置的大小,然后求和选项1使用任意精度库,因此没有上限 其他选项(失去精度)是分组求和,而不是一次求和,或者在求和之前进行除法。除了使用已经建议的更好的方法外,您还可以使用来进行计算。(请记住,它是不可变的)嗯,解决问题的最有效方法是 整理你的布景 将其拆分为一

我需要计算一组非常大的双精度(10^9个值)的平均值。这些值的总和超过了双精度的上限,那么有人知道计算平均值的一些巧妙的小技巧,而不需要同时计算总和吗


我正在使用Java1.5

您可以取不超过限制的大小相等的数字子集的平均值

将所有值除以设置的大小,然后求和

选项1使用任意精度库,因此没有上限


其他选项(失去精度)是分组求和,而不是一次求和,或者在求和之前进行除法。

除了使用已经建议的更好的方法外,您还可以使用来进行计算。(请记住,它是不可变的)

嗯,解决问题的最有效方法是

  • 整理你的布景
  • 将其拆分为一组元素,这些元素的总和不会溢出-因为它们是经过排序的,所以这既快速又简单
  • 对每组进行求和,然后除以组大小
  • 做组和的总和(可能递归调用相同的算法)-请注意,如果组的大小不相等,则必须根据它们的大小对它们进行加权

  • 这种方法的一个优点是,如果你有大量的元素要求和,并且有大量的处理器/机器用来做数学运算,它可以很好地扩展。一个double可以除以2的幂而不损失精度。所以,如果你唯一的问题是,如果求和的绝对大小,你可以在求和之前预先调整你的数字。但是,对于这样一个大小的数据集,仍然存在这样一种风险,即您将遇到这样一种情况:在大数据集上添加小数字,而小数字最终将大部分(或完全)被忽略

    例如,将2.2e-20添加到9.0e20时,结果为9.0e20,因为一旦调整了刻度,使其数字可以相加,则较小的数字为0。Double只能容纳约17位数字,如果要将这两个数字相加而不丢失,则需要超过40位数字

    因此,根据您的数据集以及您能够承受的精度数字的多少,您可能需要做其他事情。将数据分成几组会有所帮助,但保持精度的更好方法可能是确定一个粗略的平均值(您可能已经知道这个数字)。然后在求和之前从粗略平均值中减去每个值。这样你就可以把距离和平均值相加,所以你的总和永远不会太大


    然后取平均差值,并将其加到粗略总和中,得到正确的平均值。跟踪最小和最大增量也会告诉您在求和过程中损失了多少精度。如果您有很多时间并且需要非常准确的结果,您可以迭代。

    我想问您的第一个问题是:

    • 您事先知道值的数量吗
    如果没有,那么你就别无选择,只能求和、数、除,然后求平均值。如果
    Double
    的精度不够高,无法处理此问题,那么很不幸,您不能使用
    Double
    ,您需要找到一种能够处理此问题的数据类型

    另一方面,如果您事先确实知道值的数量,那么您可以查看您真正在做什么,并改变您的操作方式,但保留总体结果

    存储在某个集合A中的N个值的平均值如下:

    A[0]   A[1]   A[2]   A[3]          A[N-1]   A[N]
    ---- + ---- + ---- + ---- + .... + ------ + ----
     N      N      N      N               N       N
    
    要计算此结果的子集,可以将计算拆分为大小相等的集合,因此可以对3值集合执行此操作(假设值的数量可被3整除,否则需要不同的除数)

    请注意,您需要大小相同的集合,否则最后一个集合中的数字(与之前的所有集合相比没有足够的值)将对最终结果产生更大的影响

    按顺序考虑数字1-7,如果选择一组大小3,则会得到以下结果:

    / 1   2   3 \   / 4   5   6 \   / 7 \ 
    | - + - + - | + | - + - + - | + | - |
    \ 3   3   3 /   \ 3   3   3 /   \ 3 /
     -----------     -----------     ---
          y               y           y
    
    其中:

         2   5   7/3
         - + - + ---
         y   y    y
    
    2*3   5*3    7
    --- + --- + ---
     9     9     9
    
    如果所有集合的y为3,则得到:

         2   5   7/3
         - + - + ---
         3   3    3
    
    其中:

         2   5   7/3
         - + - + ---
         y   y    y
    
    2*3   5*3    7
    --- + --- + ---
     9     9     9
    
    即:

    6   15   7
    - + -- + -
    9    9   9
    
    总计:

    28
    -- ~ 3,1111111111111111111111.........1111111.........
     9
    
    1-7的平均值是4。显然这行不通。请注意,如果您使用数字1、2、3、4、5、6、7、0、0(注意结尾处的两个零)进行上述练习,那么您将得到上述结果

    换句话说,如果无法将值的数量拆分为大小相等的集合,则最后一个集合将被计数,就好像它与之前的所有集合具有相同的值数量一样,但对于所有缺少的值,它将被填充为零

    因此,您需要大小相同的集合。如果您的原始输入集由素数个值组成,则会很倒霉


    但我担心的是精度的损失。我不完全确定在这种情况下,如果一开始不能容纳所有值的总和,
    Double
    是否能提供足够的精度。

    请查看该部分,以便了解,因此我不再重复太多,让我声明,我假设数字列表是正态分布的,在溢出之前,你可以对许多数字求和。这项技术仍然适用于非正常发行版,但有些东西不能满足我下面描述的期望

    --

    总结一个子系列,记录你吃了多少,直到你接近溢出,然后取平均值。这将给你一个平均值a0,并计算n0。重复上述步骤,直到您将列表中的内容用尽。现在你应该有很多ai,倪

    每个ai和ni应该相对接近,但列表中最后一个可能的例外。您可以通过在列表末尾附近咬得太少来缓解这种情况

    您可以通过选取子集中的任何ni(称之为np)并除以该子集中的所有ni来组合这些ai,ni的任何子集
    static IEnumerable<uint> GetSeries(uint lowerbound, uint upperbound){
        for(uint i = lowerbound; i <= upperbound; i++)
            yield return i;
    }
    
    static void Test(){
        Console.BufferHeight = 1200;
        Random rnd = new Random();
    
        for(int i = 0; i < 1000; i++){
            uint[] numbers = new uint[rnd.Next(1, 1000)];
            for(int j = 0; j < numbers.Length; j++)
                numbers[j] = (uint)rnd.Next();
    
            double sum = 0;
            foreach(uint n in numbers)
                sum += n;
    
            double avg = sum / numbers.Length;
            double ans = new BigMeanSet().GetAverage(numbers);
    
            Console.WriteLine("{0}: {1} - {2} = {3}", numbers.Length, avg, ans, avg - ans);
    
            if(avg != ans)
                Debugger.Break();
        }
    
        for(int i = 0; i < 100; i++){
            uint length     = (uint)rnd.Next(100000, 1000000001);
            uint lowerbound = (uint)rnd.Next(int.MaxValue - (int)length);
            uint upperbound = lowerbound + length;
    
            double avg = ((double)lowerbound + upperbound) / 2;
            double ans = new BigMeanSet().GetAverage(GetSeries(lowerbound, upperbound));
    
            Console.WriteLine("{0}: {1} - {2} = {3}", length, avg, ans, avg - ans);
    
            if(avg != ans)
                Debugger.Break();
        }
    }
    
    mean(X) = sum(X[i] - c)  +  c
              -------------
                    N
    
    public static double sum(double[] numbers) { 
      double eachSum, tempSum;
      double factor = Math.pow(2.0,30); // about as large as 10^9
      for (double each: numbers) {
        double temp = each / factor;
        if (t * factor != each) {
          eachSum += each;
        else {
          tempSum += temp;
        }
      }
      return (tempSum / numbers.length) * factor + (eachSum / numbers.length);
    }
    
    double mean(double[] ary) {
      double avg = 0;
      int t = 1;
      for (double x : ary) {
        avg += (x - avg) / t;
        ++t;
      }
      return avg;
    }
    
    avg(n1)         : n1                               = a1
    avg(n1, n2)     : ((1/2)*n1)+((1/2)*n2)            = ((1/2)*a1)+((1/2)*n2) = a2
    avg(n1, n2, n3) : ((1/3)*n1)+((1/3)*n2)+((1/3)*n3) = ((2/3)*a2)+((1/3)*n3) = a3
    
    static double GetAverage(IEnumerable<double> values) {
        int i = 0;
        double avg = 0.0;
        foreach (double value in values) {
            avg = (((double)i / (double)(i + 1)) * avg) + ((1.0 / (double)(i + 1)) * value);
            i++;
        }
    
        return avg;
    }
    
    static double GetAverage(IEnumerable<double> values) {
        int i = 1;
        double avg = 0.0;
        foreach (double value in values) {
            avg += (value - avg) / (i++);
        }
    
        return avg;
    }
    
    static IEnumerable<double> GetRandomDoubles(long numValues, double maxValue, int seed) {
        Random r = new Random(seed);
        for (long i = 0L; i < numValues; i++)
            yield return r.NextDouble() * maxValue;
    
        yield break;
    }
    
    long N = 100L;
    double max = double.MaxValue * 0.01;
    
    IEnumerable<double> doubles = GetRandomDoubles(N, max, 0);
    double oldWay = GetAverage_old(doubles); // 1.00535024998431E+306
    double newWay = GetAverage(doubles); // 1.00535024998431E+306
    
    doubles = GetRandomDoubles(N, max, 1);
    oldWay = GetAverage_old(doubles); // 8.75142021696299E+305
    newWay = GetAverage(doubles); // 8.75142021696299E+305
    
    doubles = GetRandomDoubles(N, max, 2);
    oldWay = GetAverage_old(doubles); // 8.70772312848651E+305
    newWay = GetAverage(doubles); // 8.70772312848651E+305
    
    long N = 1000000000;
    double max = 100.0; // we start small, to verify accuracy
    
    IEnumerable<double> doubles = GetRandomDoubles(N, max, 0);
    double oldWay = GetAverage_old(doubles); // 49.9994879713857
    double newWay = GetAverage(doubles); // 49.9994879713868 -- pretty close
    
    max = double.MaxValue * 0.001; // now let's try something enormous
    
    doubles = GetRandomDoubles(N, max, 0);
    oldWay = GetAverage_old(doubles); // Infinity
    newWay = GetAverage(doubles); // 8.98837362725198E+305 -- no overflow
    
    BigDecimal average(double[] values){
        BigDecimal totalSum = BigDecimal.ZERO;
        double tempSum = 0.00;
        for (double value : values){
            if (isOutOfRange(tempSum, value)) {
                totalSum = sum(totalSum, tempSum);
                tempSum = 0.00;
            }
            tempSum += value;
        }
        totalSum = sum(totalSum, tempSum);
        BigDecimal count = new BigDecimal(values.length);
        return totalSum.divide(count);
    }
    
    BigDecimal sum(BigDecimal val1, double val2){
        BigDecimal val = new BigDecimal(String.valueOf(val2));
        return val1.add(val);
    }
    
    boolean isOutOfRange(double sum, double value){
        // because sum + value > max will be error if both sum and value are positive
        // so I adapt the equation to be value > max - sum 
        if(sum >= 0.00 && value > Double.MAX - sum){
            return true;
        }
    
        // because sum + value < min will be error if both sum and value are negative
        // so I adapt the equation to be value < min - sum
        if(sum < 0.00 && value < Double.MIN - sum){
            return true;
        }
        return false;
    }