如何解决如何在没有三个嵌套循环的情况下对三次和编码
我正在尝试有效地实现类似以下的公式:
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]
}
与简单代码相比,它给出了不同的答案,但是它确实消除了一层迭代,所以我认为我处在正确的轨道上(尽管出轨)。有人可以帮忙吗?
解决方法
首先要进行的观察是,由于x_i和x_j相对于k是常数,并且被相乘,因此您可以将它们从求和中排除,从而得到:
现在,您可以看到只需要为每个i < 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));
}
}
,
以下更新基于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
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。