最有效的算法来计算分数总和的常用分子

时间:2011-07-27 07:41:41

标签: algorithm math

我很确定这是这个问题的正确网站,但如果它更适合那里,请随意将其移至其他堆栈交换网站。

假设您有一个分数a1/d1 + a2/d2 + … + an/dn的总和。您想要计算一个公共分子和分母,即将其重写为p/q。我们有公式

p = a1*d2*…*dn + d1*a2*d3*…*dn + … + d1*d2*…d(n-1)*an
q = d1*d2*…*dn.

计算这些内容的最有效方法是什么,尤其是p?你可以看到,如果你天真地计算它,即使用我上面给出的公式,你会计算很多冗余的东西。例如,您将计算d1*d2 n-1次。

我的第一个想法是迭代计算d1*d2d1*d2*d3,...和dn*d(n-1)dn*d(n-1)*d(n-2),......但即使这样效率也很低,因为你最终会计算乘法在“中间”两次(例如,如果n足够大,您将计算两次d3*d4

我确信这个问题可以用某种图形理论或组合学来表达,但我还没有研究过足够的东西以便对它有一个良好的感觉。

还有一个注意事项:我不关心取消,只是最有效的繁殖方式。

更新:

我应该知道stackoverflow上的人会假设这些是数字,但我已经习惯了我的用例,我忘了提这个。

我们不能只从每个术语中“划分”an。这里的用例是一个符号系统。实际上,我正在尝试在SymPy computer algebra system中修复一个名为.as_numer_denom()的函数,该函数目前以天真的方式计算它。请参阅corresponding SymPy issue

划分事情有一些问题,我想避免。首先,不能保证事情会取消。这是因为在数学上,(a*b)**n != a**n*b**n一般来说(如果ab是正数,但是例如,如果a == b ==-1n == 1/2,则得到(a*b)**n == 1**(1/2) == 1 1}}但是(-1)**(1/2)*(-1)**(1/2) == I*I == -1)。所以我认为假设除以an将在表达式中取消它(这可能实际上是没有根据的,我需要检查代码的作用)并不是一个好主意。

其次,我还想将此算法应用于计算有理函数的总和。在这种情况下,这些项将自动乘以一个多项式,并且“除去”每个an将涉及应用多项式除法算法。你可以看到,在这种情况下,你真的想要首先计算最有效的乘法。

更新2:

我认为我对取消象征性术语的担忧可能毫无根据。 SymPy不会自动取消x**n*x**(m - n)之类的内容,但我认为通过乘法组合的任何指数也会通过除法组合,因此权力应该取消。

常量会自动分配添加内容,例如:

In [13]: 2*(x + y)*z*(S(1)/2)
Out[13]: 
z⋅(2⋅x + 2⋅y)
─────────────
      2      

但这是第一个bug,第二个永远不会成为问题(我认为),因为1/2会被12分解为算法每个术语的分子和分母。

尽管如此,我仍然想知道如何在不从每个术语“分割”di的情况下做到这一点,这样我就可以有一个有效的算法来总结合理的函数。

4 个答案:

答案 0 :(得分:3)

计算两个新数组:

第一个包含左侧的部分倍数:l[0] = 1, l[i] = l[i-1] * d[i]

第二个包含右侧的部分倍数:r[n-1] = 1, r[i] = d[i] * r[i+1]

在这两种情况下,1都是你工作的戒指的乘法身份。

然后,您的每个字词都位于顶部,t[i] = l[i-1] * a[i] * r[i+1]

这假设乘法是关联的,但它不一定是可交换的。

作为第一个优化,您实际上不必将r创建为数组:您可以执行第一次传递来计算所有l值,并累积r在第二次(向后)传递期间的值以计算加数。无需实际存储r值,因为您按顺序使用每个值。

在你的问题中,你说这会计算d3*d4两次,但事实并非如此。它确实将两个不同的值乘以d4(一个是右乘,另一个是左乘),但这并不是一个重复的操作。无论如何,对于在非交换乘法或非场环中不起作用的另一种方法,乘法的总数约为4*n,而2*n乘法和n除法。

答案 1 :(得分:2)

如果你想在上面的表达式中计算p,一种方法是将所有分母相乘(在O(n)中,其中n是分数的数量),让这个值为D。然后,迭代所有分数,对于每个分数,分子a i 和分母d i ,计算 i * D / d <子> I 。最后一个术语等于分数的分子和除了它自己之外的所有分母的乘积。这些术语中的每一个都可以在O(1)时间内计算(假设您正在使用硬件乘法,否则可能需要更长时间),并且您可以在O(n)时间内对它们进行求和。

这给出了一个O(n)时算法,用于计算新分数的分子和分母。

答案 2 :(得分:2)

不是一次性添加n商,而是使用成对添加商。

  • 如果事物在部分和中抵消,则数字或多项式保持较小,这使计算更快。

  • 您可以避免多次计算同一产品的问题。

您可以尝试以某种方式订购添加内容,以便更有可能取消(可能先添加小分母的商数?),但我不知道这是否值得。

如果你从头开始,这更容易实现,但我不确定它是否适合替代SymPy中有问题的例程。

修改:为了使其更明确,我建议将a1/d1 + a2/d2 + … + an/dn计算为(…(a1/d1 + a2/d2) + … ) + an/dn

答案 3 :(得分:0)

我还指出,你可以手动筛选出共同的分母,并将这些分组合并而不是乘法。