计算所有 k 乘积之和的高效算法

发布于 2025-01-05 17:17:32 字数 306 浏览 1 评论 0 原文

假设给您一个由 n 个数字组成的列表 L 和一个整数 k。有没有一种有效的方法来计算Lk个不同数字的所有乘积之和?

L=[1,3,4,6]k=2 为例。那么我要查找的数字是

1*3 + 1*4 + 1*6 + 3*4 + 3*6 + 4*6

你能想出一种方法来避免生成所有大小为 k 的子集吗?

Suppose you are given a list L of n numbers and an integer k<n. Is there an efficient way to calculate the sum of all products of k distinct numbers in L?

As an example, take L=[1,3,4,6] and k=2. Then the number I am looking for is

1*3 + 1*4 + 1*6 + 3*4 + 3*6 + 4*6.

Can you think of a way of doing it which avoids generating all the subsets of size k?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

久光 2025-01-12 17:17:32

令 F(X,k,n) 为数组 X 的前 n 个元素的 k 乘积和。

F(X,k,n) = F(X,k,n-1)+F(X,k-1) ,n-1)*X[n]

您可以使用动态规划来解决。复杂度 = O(kn)。

F(X,k,n) 的结束条件: 当 n=k F(X,k,k) = X[1]* X[2]*...*X[n]

更多详细信息:

F(X,1,1) = X[1]
F(X,1,i) = F(X,1,i-1)+X[i] for i=2...n 

For j=2..n:
    For i = 1..k:
        if i<j:
            F(X,i,j) = F(X,i,j-1)+F(X,i-1,j-1)*X[j]
        else if i==j:
            F(X,i,j) = F(X,i-1,j-1)*X[j]
        else:
            pass

Let F(X,k,n) be the k-product sum of first n elements of array X.

F(X,k,n) = F(X,k,n-1)+F(X,k-1,n-1)*X[n]

which you can solve using dynamic programming. Complexity = O(kn).

End conditions for F(X,k,n): When n=k F(X,k,k) = X[1]* X[2]*...*X[n]

More details:

F(X,1,1) = X[1]
F(X,1,i) = F(X,1,i-1)+X[i] for i=2...n 

For j=2..n:
    For i = 1..k:
        if i<j:
            F(X,i,j) = F(X,i,j-1)+F(X,i-1,j-1)*X[j]
        else if i==j:
            F(X,i,j) = F(X,i-1,j-1)*X[j]
        else:
            pass
陌上青苔 2025-01-12 17:17:32

是的,有办法。考虑多项式

(X + a[0]) * (X + a[1]) * ... * (X + a[n-1])

它的系数只是k乘积的总和,其次数为n,因此您可以计算所有k的总和-在 O(n^2) 步中同时计算所有 k 的产品。

经过s步后,Xsk的系数是第一个s数组的k乘积之和元素。前 s+1 元素的 k 乘积分为两类,涉及 (s+1)st< /sup> 元素 - 这些具有形式 a[s]*((k-1) - 第一个 s 元素的乘积) - 以及那些不涉及它的 - 这些是第一个的 k 个乘积s 元素。

代码使得 result[i] 是 Xi 的系数((ni)-乘积之和):

int *k_products_1(int *a, int n){
    int *new, *old = calloc((n+1)*sizeof(int));
    int d, i;
    old[0] = 1;
    for(d = 1; d <= n; ++d){
        new = calloc((n+1)*sizeof(int));
        new[0] = a[d-1]*old[0];
        for(i = 1; i <= d; ++i){
            new[i] = old[i-1] + a[d-1]*old[i];
        }
        free(old);
        old = new;
    }
    return old;
}

如果您只想一个 kk 乘积之和,您可以在索引 nk 处停止计算,给出 O(n*(nk) ) 算法 - 如果 k >= 就很好n/2。要获得 k <= n/2 的 O(n*k) 算法,您必须以相反的方式组织系数数组,以便 result[k] 是 Xnk 的系数(如果您只需要一个和,则在索引 k 处停止计算):

int *k_products_2(int *a, int n){
    int *new, *old = calloc((n+1)*sizeof(int));
    int d, i;
    old[0] = 1;
    for(d = 1; d <= n; ++d){
        new = calloc((n+1)*sizeof(int));
        new[0] = 1;
        for(i = 1; i <= d; ++i){
            new[i] = old[i] + a[d-1]*old[i-1];
        }
        free(old);
        old = new;
    }
    return old;
}

Yes, there is a way. Consider the polynomial

(X + a[0]) * (X + a[1]) * ... * (X + a[n-1])

Its coefficients are just the sums of the k-products, its degree is n, so you can calculate the sum of all k-products for all k simultaneously in O(n^2) steps.

After s steps, the coefficient of Xs-k is the sum of the k-products of the first s array elements. The k-products of the first s+1 elements fall into two classes, those involving the (s+1)st element - these have the form a[s]*((k-1)-product of the first s elements) - and those not involving it - these are the k-products of the first s elements.

Code such that result[i] is the coefficient of Xi (the sum of the (n-i)-products):

int *k_products_1(int *a, int n){
    int *new, *old = calloc((n+1)*sizeof(int));
    int d, i;
    old[0] = 1;
    for(d = 1; d <= n; ++d){
        new = calloc((n+1)*sizeof(int));
        new[0] = a[d-1]*old[0];
        for(i = 1; i <= d; ++i){
            new[i] = old[i-1] + a[d-1]*old[i];
        }
        free(old);
        old = new;
    }
    return old;
}

If you only want the sum of the k-products for one k, you can stop the calculation at index n-k, giving an O(n*(n-k)) algorithm - that's good if k >= n/2. To get an O(n*k) algorithm for k <= n/2, you have to organise the coefficient array the other way round, so that result[k] is the coefficient of Xn-k (and stop the calculation at index k if you want only one sum):

int *k_products_2(int *a, int n){
    int *new, *old = calloc((n+1)*sizeof(int));
    int d, i;
    old[0] = 1;
    for(d = 1; d <= n; ++d){
        new = calloc((n+1)*sizeof(int));
        new[0] = 1;
        for(i = 1; i <= d; ++i){
            new[i] = old[i] + a[d-1]*old[i-1];
        }
        free(old);
        old = new;
    }
    return old;
}
匿名。 2025-01-12 17:17:32

您可以探索的一个有趣的属性是乘法相对于加法的分配属性。

L=[a,b,c,d]

当 k = 1 时,这是微不足道的:

S=a+b+c+d

当 k = 2 时:

S = a * (b + c + d) + b * (c + d) + c * d

当 k = 3 时,事情会变得更有趣:

S = a * b * ( c + d) + (c * d) * (a + b)
S = a * (b * (c + d)) + c * d) + b * (c * d)  <-- this form lends itself better to the algorithm

对于 k = 4: 对于

S = a * b * c * d

较大的 n 值,这应该成立。

C# 中的实现:

 private static int ComputeSum(int[] array, int offset, int K)
 {
     int S = 0;

     if (K == 1)
     {                      
         for (int i = offset; i < array.Length; i++)
             S += array[i];
     }
     else if ((array.Length - offset) == K)
     {
         S = array[offset] * ComputeSum(array, offset + 1, K - 1);
     }
     else if (offset < array.Length)
     {
         S = ComputeSum(array, offset + 1, K) + array[offset] * ComputeSum(array, offset + 1, K - 1);             
     }

     return S;
 }

可以通过记忆进一步改进。

An interesting property you could explore is the distributive property of multiplication in relation to addition.

L=[a,b,c,d]

When k = 1, it's trivial:

S=a+b+c+d

When k = 2:

S = a * (b + c + d) + b * (c + d) + c * d

When k = 3, things get a bit more interesting:

S = a * b * ( c + d) + (c * d) * (a + b)
S = a * (b * (c + d)) + c * d) + b * (c * d)  <-- this form lends itself better to the algorithm

And for k = 4:

S = a * b * c * d

This should hold for larger values of n.

An implementation in C#:

 private static int ComputeSum(int[] array, int offset, int K)
 {
     int S = 0;

     if (K == 1)
     {                      
         for (int i = offset; i < array.Length; i++)
             S += array[i];
     }
     else if ((array.Length - offset) == K)
     {
         S = array[offset] * ComputeSum(array, offset + 1, K - 1);
     }
     else if (offset < array.Length)
     {
         S = ComputeSum(array, offset + 1, K) + array[offset] * ComputeSum(array, offset + 1, K - 1);             
     }

     return S;
 }

Which can be further improved by memoization.

爱的故事 2025-01-12 17:17:32

您可以将 k 减少 1:

例如,对于 k=2

1*3 + 1*4 + 1*6 + 3*4 + 3*6 + 4*6

==

1*(3+4+6)+3*(4+6)+4*6

和 k=3

1*3*4 + 1*3*6 + 3*4*6

==

1*3*(4+6) + 3*4*6

所以基本上您循环列表,然后递归到相同的算法,其中 k 减少 1 并且仅列表的其余部分

You can reduce k by 1:

e.g. for k=2

1*3 + 1*4 + 1*6 + 3*4 + 3*6 + 4*6

==

1*(3+4+6)+3*(4+6)+4*6

and for k=3

1*3*4 + 1*3*6 + 3*4*6

==

1*3*(4+6) + 3*4*6

So basically you cycle your list, then recurse to the same algorithm with k reduced by 1 and only the rest of the list

优雅的叶子 2025-01-12 17:17:32

对于 k=2,

让我们 s = SUM_x_in_L x (数字之和)和 sq = SUM_x_in_L x^2 (平方和)

,那么它就是 SUM_x_in_L ( s - x) * x / 2 = (s * s - sq) / 2

For k=2,

let's s = SUM_x_in_L x (sum of the numbers) and sq = SUM_x_in_L x^2 (sum of the squares)

then it's SUM_x_in_L (s - x) * x / 2 = (s * s - sq) / 2

最后的乘客 2025-01-12 17:17:32

从代数角度来说,对于k=2,只需取L 的元素之和,平方,然后减去L 的平方和。也就是说:

int sum = 0;
int sqSum = 0;
for (int i=0; i<n; ++i) {
    sum += L[i];
    sqSum += L[i]*L[i];
}
return sum*sum - sqSum;

在您的示例中,您正在计算的是这个

(1 + 3 + 4 + 6)^2 - (1^2 + 3^2 + 4^2 + 6^2) = 1*3 + 1*4 + 1*6 + 3*4 + 3*6 + 4*6

这应该会提示您一般如何进行。

Algebraically, for k=2 just take the sum of the elements of L, square it, and subtract the sum of the squares of L. That is:

int sum = 0;
int sqSum = 0;
for (int i=0; i<n; ++i) {
    sum += L[i];
    sqSum += L[i]*L[i];
}
return sum*sum - sqSum;

In your example, what you are computing is this

(1 + 3 + 4 + 6)^2 - (1^2 + 3^2 + 4^2 + 6^2) = 1*3 + 1*4 + 1*6 + 3*4 + 3*6 + 4*6

This should give you a hint for how to proceed in general.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文