Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most efficient algorithm to compute a common numerator of a sum of fractions

Tags:

algorithm

math

I'm pretty sure that this is the right site for this question, but feel free to move it to some other stackexchange site if it fits there better.

Suppose you have a sum of fractions a1/d1 + a2/d2 + … + an/dn. You want to compute a common numerator and denominator, i.e., rewrite it as p/q. We have the formula

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

What is the most efficient way to compute these things, in particular, p? You can see that if you compute it naïvely, i.e., using the formula I gave above, you compute a lot of redundant things. For example, you will compute d1*d2 n-1 times.

My first thought was to iteratively compute d1*d2, d1*d2*d3, … and dn*d(n-1), dn*d(n-1)*d(n-2), … but even this is inefficient, because you will end up computing multiplications in the "middle" twice (e.g., if n is large enough, you will compute d3*d4 twice).

I'm sure this problem could be expressed somehow using maybe some graph theory or combinatorics, but I haven't studied enough of that stuff to have a good feel for it.

And one note: I don't care about cancelation, just the most efficient way to multiply things.

UPDATE:

I should have known that people on stackoverflow would be assuming that these were numbers, but I've been so used to my use case that I forgot to mention this.

We cannot just "divide" out an from each term. The use case here is a symbolic system. Actually, I am trying to fix a function called .as_numer_denom() in the SymPy computer algebra system which presently computes this the naïve way. See the corresponding SymPy issue.

Dividing out things has some problems, which I would like to avoid. First, there is no guarantee that things will cancel. This is because mathematically, (a*b)**n != a**n*b**n in general (if a and b are positive it holds, but e.g., if a == b ==-1 and n == 1/2, you get (a*b)**n == 1**(1/2) == 1 but (-1)**(1/2)*(-1)**(1/2) == I*I == -1). So I don't think it's a good idea to assume that dividing by an will cancel it in the expression (this may be actually be unfounded, I'd need to check what the code does).

Second, I'd like to also apply a this algorithm to computing the sum of rational functions. In this case, the terms would automatically be multiplied together into a single polynomial, and "dividing" out each an would involve applying the polynomial division algorithm. You can see in this case, you really do want to compute the most efficient multiplication in the first place.

UPDATE 2:

I think my fears for cancelation of symbolic terms may be unfounded. SymPy does not cancel things like x**n*x**(m - n) automatically, but I think that any exponents that would combine through multiplication would also combine through division, so powers should be canceling.

There is an issue with constants automatically distributing across additions, like:

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

But this is first a bug and second could never be a problem (I think) because 1/2 would be split into 1 and 2 by the algorithm that gets the numerator and denominator of each term.

Nonetheless, I still want to know how to do this without "dividing out" di from each term, so that I can have an efficient algorithm for summing rational functions.

like image 891
asmeurer Avatar asked Jul 27 '11 07:07

asmeurer


2 Answers

Instead of adding up n quotients in one go I would use pairwise addition of quotients.

  • If things cancel out in partial sums then the numbers or polynomials stay smaller, which makes computation faster.

  • You avoid the problem of computing the same product multiple times.

You could try to order the additions in a certain way, to make canceling more likely (maybe add quotients with small denominators first?), but I don't know if this would be worthwhile.

If you start from scratch this is simpler to implement, though I'm not sure it fits as a replacement of the problematic routine in SymPy.

Edit: To make it more explicit, I propose to compute a1/d1 + a2/d2 + … + an/dn as (…(a1/d1 + a2/d2) + … ) + an/dn.

like image 149
starblue Avatar answered Sep 23 '22 08:09

starblue


Compute two new arrays:

The first contains partial multiples to the left: l[0] = 1, l[i] = l[i-1] * d[i]

The second contains partial multiples to the right: r[n-1] = 1, r[i] = d[i] * r[i+1]

In both cases, 1 is the multiplicative identity of whatever ring you are working in.

Then each of your terms on the top, t[i] = l[i-1] * a[i] * r[i+1]

This assumes multiplication is associative, but it need not be commutative.

As a first optimization, you don't actually have to create r as an array: you can do a first pass to calculate all the l values, and accumulate the r values during a second (backward) pass to calculate the summands. No need to actually store the r values since you use each one once, in order.

In your question you say that this computes d3*d4 twice, but it doesn't. It does multiply two different values by d4 (one a right-multiplication and the other a left-multiplication), but that's not exactly a repeated operation. Anyway, the total number of multiplications is about 4*n, vs. 2*n multiplications and n divisions for the other approach that doesn't work in non-commutative multiplication or non-field rings.

like image 41
Steve Jessop Avatar answered Sep 21 '22 08:09

Steve Jessop