Suppose you are given a list L
of n
numbers and an integer k
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.
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
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
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.
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
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;
}