有效的方法来进行求和

时间:2012-04-30 11:46:25

标签: c arrays algorithm

是否有任何有效的技术可以进行以下求和?

给定有限集 A 包含 n 整数 A = {X1,X2,...,Xn} ,其中 Xi < / em>是一个整数。现在有 n A 的子集,由 A1,A2,...,An 表示。我们想要计算每个子集的总和。有一些有效的技术吗?

(请注意, n 通常大于 A 的所有子集的平均大小。)

例如,如果 A = {1,2,3,4,5,6,7,9} A1 = {1,3,4,5} A2 = {2,3,4} A3 = ... 。一种计算 A1 A2 总和的简单方法需要5个触发器才能添加:

SUM(A1)= 1 + 3 + 4 + 5 = 13

萨姆(A2)= 2 + 3 + 4 = 9

...

现在,如果首先计算3 + 4,然后记录其结果7,我们只需要3个Flops来添加:

SUM(A1)= 1 + 7 + 5 = 13

萨姆(A2)= 2 + 7 = 9

...

一般情况怎么样?有没有有效的方法来加速计算?谢谢!

5 个答案:

答案 0 :(得分:2)

假设'addition'不仅仅是ADD操作,而是一些涉及两个整数操作数的非常密集的函数,那么一个明显的方法就是缓存结果。

您可以通过合适的数据结构实现这一目标,例如包含由两个操作数组成的键的键值字典,并将答案作为值。

但是当您在问题中指定C时,最简单的方法是nn整数数组,其中x + y的解决方案存储在array[x][y]

然后,您可以重复迭代子集,并为每对操作数检查数组中的适当位置。如果没有值,则必须计算它并将其放入数组中。然后,该值将替换子集中的两个操作数,并进行迭代。

如果操作是可交换的,则应在查找数组之前对操作数进行排序(即,使第一个索引始终是两个操作数中最小的一个),因为这将最大化“缓存”命中。

答案 1 :(得分:2)

对于某些子集选择,有些方法可以加快计算速度,如果你不介意做一些(可能很昂贵的)预计算,但不是全部。例如,假设您的子集是{1,2},{2,3},{3,4},{4,5},...,{n-1,n},{n,1};那么天真的方法每个子集使用一个算术运算,你显然不能做得更好。另一方面,如果您的子集是{1},{1,2},{1,2,3},{1,2,3,4},...,{1,2,...,那么你可以用n-1算术运算得到,而天真的方法则要糟糕得多。

这是进行预计算的一种方法。它并不总能找到最佳结果。对于每对子集,将转换成本定义为min(对称差异的大小,Y-1的大小)。 (X和Y的对称差异是X或Y中的事物集合,但不是两者。)因此,转移成本是计算Y元素总和所需的算术运算数,给定总和X的。将空集添加到子集列表中,并使用Edmonds算法(http://en.wikipedia.org/wiki/Edmonds%27_algorithm)或更快但更复杂的变体之一计算最小成本定向生成树那个主题。现在确保当您的生成树有边X - >;你在Y之前计算X.(这是一种“拓扑排序”,可以有效地完成。)

例如,当你有{1,2},{3,4},{1,2,3,4},{5,6},{7,8},{时,这将给出明显不理想的结果5,6,7,8}。在使用上面的过程确定您的操作顺序之后,您可以执行优化过程,在这里您可以找到更便宜的方法来评估已经计算出的总和的每个集合的总和,这可能会在实践中给出相当不错的结果。

我怀疑,但没有试图证明,找到一组给定子集的最佳程序是NP难或更差。 (它肯定是可计算的;你可能做的一组可能的计算是有限的。但是,从它的角度来看,它可能是非常昂贵的;可能你可能会跟踪大约2 ^ n部分总和,在每个步骤中将它们中的任何一个添加到任何其他步骤,并且具有最多约n ^ 2个步骤,对于超级天真的成本(2 ^ 2n)^(n ^ 2)= 2 ^(2n ^ 3 )尝试各种可能性的操作。)

答案 2 :(得分:1)

常见的优化技术是预先计算中间结果。在您的情况下,您可以预先计算来自A的2个加数的所有总和,并将它们存储在查找表中。这将导致|A|*|A+1|/2表条目,其中|A|A的基数。

为了计算Ai的元素总和,你:

  • 查找Ai的前两个元素的总和并将它们保存在tmp
  • 虽然在Ai中有一个元素x:
  • 查找tmp和x的总和

为了计算示例中A1 = {1,3,4,5}的元素总和,请执行以下操作:

  • lookup(1,3)= 4
  • lookup(4,4)= 8
  • lookup(8,5)= 13

请注意,计算任何给定Ai的总和不需要求和,因为在预先计算查找表时已经完成了所有工作。

如果将查找表存储在哈希表中,则lookup()位于O(1)中。


可能对此方法进行优化:

  • 在计算求和结果的同时构造查找表;因此,您只计算实际需要的那些总和。您的查找表现在是缓存。
  • 如果您的添加操作是可交换的,则可以通过仅存储较小的加数首先出现的那些求和来节省一半的高速缓存大小。然后修改lookup(),以便lookup(a,b) = lookup(b,a) a > b

答案 3 :(得分:0)

如果假定求和是一个耗时的动作,你可以找到每对子集的LCS(假设它们按照注释中的说明进行排序,或者如果它们没有排序则对它们进行排序),之后计算LCS的总和最大长度(在所有LCS中成对),然后用相关数字替换相关数组中的值,更新它们的LCS并继续这种方式,直到没有多个LCS的数字。当然这不是最佳的,但它比天真的算法(总和的数量更少)更好。但是,您可以进行回溯以找到最佳解决方案。

例如,您的样本输入:

A1={1,3,4,5} , A2={2,3,4}

LCS (A_1,A_2) = {3,4} ==>7 ==>replace it:

A1={1,5,7}, A2={2,7} ==> LCS = {7}, maximum LCS length is `1`, so calculate sums.

仍然可以通过两个随机数的计算总和来改进它,然后再次采用LCS,......

答案 4 :(得分:0)

NO。没有高效的技术。

因为NP complete问题。而这种问题没有有效的解决方案

为什么NP完全?
我们可以使用算法解决这个问题set cover problem,只需在集合中添加额外的集合,包含所有元素。

实施例: 我们有各种要素 A1 = {1,2},A2 = {2,3},A3 = {3,4} 我们想解决集合覆盖问题。

我们添加到这个集合,包含所有元素的数字集 A4 = {1,2,3,4}

我们使用约翰史密斯正在寻求的algorhitm,我们检查解决方案A4代表whit。 我们解决了NP-Complete问题。