【问题标题】:How to code triple sum without three nested loops如何在没有三个嵌套循环的情况下编写三重和
【发布时间】:2020-11-01 22:06:30
【问题描述】:

我正在尝试有效地实现如下公式:

sum(i=1,n) sum(j=i+1,n) sum(k=j+1,n) x(i)*x(j)*x(k)

直接的方法是这样的:

sum = 0
for (int i=1; i<n; i++ )
    for( int j=i+1; j<n; j++ )
        for( int k=j+1; k<n; k++ )
            sum += x[i]*x[j]*x[k]

问题是这是O(n^3)。我想知道是否有某种方法可以重写这个 这样我就可以使用一些递归关系消除一层甚至两层迭代。我尝试了以下方法,但没有运气:

for (int i=n; i>0; i-- )
    int sumK = 0
    for( int j=n; j>i; j-- ) {
         sum += sumK
         sumK += x[i]*x[j]*x[j]
    }

它给出了与直接代码不同的答案,但它确实消除了一层迭代,所以我认为我走在正确的轨道上(尽管偏离了轨道)。有人可以帮忙吗?

【问题讨论】:

    标签: java arrays iteration time-complexity


    【解决方案1】:

    您从这个正式编写的公式开始:

    首先要观察的是,由于 x_i 和 x_j 相对于 k 是常数,并且它们正在相乘,因此您可以将它们从求和中扣除,留下:

    使用相同的逻辑,您可以将 x_i 从另一个求和中提取出来:

    现在,您可以看到,您只需为每个 i &lt; n 计算从 i..n 开始的 x 中元素的总和(可以使用后缀总和在 O(n) 时间内完成),确保乘以您开始的元素,以说明与 x_j 的乘法。这占了最右边的总和。现在,您可以做同样的事情,但对于您之前得到的总和,请确保乘以您开始的元素的值(以考虑乘以 x_i)。这占中间总和。然后,您可以将之前结果中从 1 到 n 的所有值相加,得出最终答案。这是生成的 java 代码(查看函数 fastProduct):

    import java.util.Random;
    
    public class Test {
        public static void printArr(long[] arr) {
            System.out.print("[");
            for (int i = 0; i < arr.length; i ++) {
                System.out.print(arr[i]);
                if (i < arr.length - 1) {
                    System.out.print(", ");
                }
            }
            System.out.println("]");
        }
    
        public static long naiveProduct(long[] arr) {
            long sum = 0;
            for (int i = 0; i < arr.length; i ++) {
                for (int j = i + 1; j < arr.length; j ++) {
                    for (int k = j + 1; k < arr.length; k ++) {
                        sum += arr[i] * arr[j] * arr[k];
                    }
                }
            }
            return sum;
        }
    
        public static long fastProduct(long[] arr) {
            long[] sums = new long[arr.length];
            sums[arr.length - 2] = arr[arr.length - 1];
            // pre-calculate the summations of x_k
            for (int j = arr.length - 3; j >= 1; j --) {
                sums[j] = sums[j + 1] + arr[j + 1];
            }
            // multiply by x_j
            for (int j = 1; j <= arr.length - 2; j ++) {
                sums[j] *= arr[j];
            }
            long[] sumSums = new long[arr.length];
            sumSums[arr.length - 3] = sums[arr.length - 2];
            // pre-calculate the summations of x_j times the summation of x_k
            for (int i = arr.length - 4; i >= 0; i --) {
                sumSums[i] = sumSums[i + 1] + sums[i + 1];
            }
            // multiply by x_i
            for (int i = 0; i <= arr.length - 3; i ++) {
                sumSums[i] *= arr[i];
            }
            long total = 0;
            // sum up the final summation
            for (int i = 0; i < arr.length - 2; i ++) {
                total += sumSums[i];
            }
            return total;
        }
    
        public static void main(String[] args) {
            long[] test = new long[10];
            Random rand = new Random();
            for (int i = 0; i < test.length; i ++) {
                test[i] = rand.nextInt(9) + 1;
            }
            printArr(test);
            System.out.printf("%d %d", naiveProduct(test), fastProduct(test));
        }
    }
    

    【讨论】:

    • Aplet123,谢谢,但我不确定我是否遵循。我认为你的意思是三个和中的每一个都是 O(n),所以乘积是 O(3n),也就是 O(n)。但是,例如,考虑第三个总和。因为 k 从 j+1 运行到 n,该求和实际上是“j”以及 Xk 的函数,所以实际上,实际上有 n 个不同的求和结果(每个 j 一个),所以你有什么我认为得到的是 O(n)*O(n)*O(n) 或 O(n3)。但是,我可能没有正确遵循您的建议;您能否添加一些(伪)代码来说明您的建议?
    • 不错!由于多个 O(n) 循环的串联(不是嵌套),显然是 O(n)。看来,关键在于您通过维护一个 array 和而不是单个总和来明确内部总和的可变性。谢谢!
    【解决方案2】:

    以下更新基于earlier Aplet123's answer,由于重用k 上的最内层和,它允许通过删除一个嵌套循环来降低复杂性:

    static int sum3_fix(int[] x) {
        int sum = 0;
        int tmp = 0; // sum of innermost loop, will be reduced for the following iterations
        int n = x.length;
    
        for (int i = 0; i < n; i++ ) {
            int tmp2 = 0;
            for(int j = i + 1; j < n; j++ ) {
                if (tmp == 0) { // calculate sum of x[2,n] only once
                    for(int k = j + 1; k < n; k++ ) {
                        tmp += x[k];
                    }
                } else {
                    tmp -= x[j];
                }
                tmp2 += x[j] * tmp;
            }
            sum += x[i] * tmp2;
        }
        return sum;
    }
    

    测试:

    int[] x = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 10, 15, 20};
    System.out.println(sum3(x));  // non-optimized implementation
    System.out.println(sum3_fix(x));
    

    输出

    55506
    55506
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多