假设我们有一套
{a_1, a_2, a_3, ..., a_n}
目标是找到我们通过以下方式创建的总和:我们找到长度为 3 的所有子集,然后将每个子集的元素相乘(对于子集{b_1, b_2, b_3}
,结果将为b_1*b_2*b_3
)。最后我们总结了所有这些产品。
我正在寻找一种最短时间执行算法。
例子
SET: {3, 2, 1, 2}
Let S be our sum.
S = 3*2*1 + 3*2*2 + 2*1*2 + 3*1*2 = 28
假设我们有一套
{a_1, a_2, a_3, ..., a_n}
目标是找到我们通过以下方式创建的总和:我们找到长度为 3 的所有子集,然后将每个子集的元素相乘(对于子集{b_1, b_2, b_3}
,结果将为b_1*b_2*b_3
)。最后我们总结了所有这些产品。
我正在寻找一种最短时间执行算法。
例子
SET: {3, 2, 1, 2}
Let S be our sum.
S = 3*2*1 + 3*2*2 + 2*1*2 + 3*1*2 = 28
这是一种O(n^2)
方法:
sum = SUM(list)
answer = 0
for each i from 0 to n:
sum -= list[i]
remains = sum
for each j from i+1 to n:
remains -= list[j]
answer += list[i] * list[j] * (remains)
它之所以有效,是因为x,y
您需要对每两个元素求和x*y*z
(对于所有元素z
),但所有可能z
值的总和是SUM(list) - x - y
.
所以,而不是做: x*y*z1 + x*y*z2 + ... + x*y*z(n-2)
,你基本上做x*y*(z1 + ... + z(n-2))
编辑:如@AbhishekBansal 所述,由于仅在“尾部”中不相乘,因此编辑了多次计数。您只需将每个元素与列表的“尾部”相乘,其中尾部是 中最后一个元素之后的所有元素x,y
。
C++ 中的完整工作代码(跟进 Amit 的想法)
#include <iostream>
using namespace std;
int main()
{
int s[] = {3, 2, 1, 2};
double sumOfFullList = 0;
for ( int i = 0; i < 4; i++ )
sumOfFullList += s[i];
double sum = 0;
for ( int i = 0; i < 4; i++ ) {
double sumOfList = sumOfFullList - s[i];
sumOfFullList -= s[i];
for ( int j = i+1; j < 4; j++ ) {
sumOfList -= s[j];
sum += s[i]*s[j]*(sumOfList);
//cout << s[i] << " " << s[j] << " " << sumOfList;
}
}
cout << sum << endl;
return 0;
}
输出:
28
当允许重复时(如 a_1*a_1*a_1)更容易计算相乘的三元组的总和。这个总和只是(sum^3)
。
由于不允许重复,我们可以减去它们:(sum^3 - 3*sumsquares*sum)
.
但是上面的公式将主对角线上的元素减去 3 次,而应该只减去一次。需要对此进行补偿:(sum^3 - 3*sumsquares*sum + 2*sumcubes)
。
上面的公式没有考虑3!每个三元组的排列。所以应该除以3!
。
最后我们有一个线性时间算法:
result = (sum^3 - 3*sumsquares*sum + 2*sumcubes)/6