Polynomial long division

I re-learned about long division with polynomials this week. It was kind of obvious that you can do this because if you can multiply polynomials it means that you can also divide them.

But the technique to actually doing it on the paper is interesting. It also adapts to the code, this code is taken from the wikipedia page "polynomial long division":

assert d != 0, "divide by zero"
q = 0
r = n # the whole polynomial is a remainder at first.
while r != 0 and degree(r) >= degree(d):
    t = lead(r) / lead(d) # dividing leading terms
    q = q + t
    r = r - t * d
return [q, r]

To get the degree(r) of a polynomial r, you have to pull the polynomial into its canonical form, and pick the degree of the largest term.

Monomials

The lead(r) returns a leading term of the polynomial, implying that there is an order between the terms. When you pick a term of a polynomial, the term is a monomial. Ordering between them is called 'monomial ordering'.

To get it into the right order here you have to order the terms by a degree, largest degree first. This way also calculating the degree of the polynomial is easy. It is always the leading term that has the highest degree.

Multivariate division

The above applies to polynomials with one variable, but division can be also defined on polynomials with multiple variables.

With multiple variables you have to consider division with multiple terms. The code would change to this:

assert d != 0, "divide by zero"
q = 0
r = n
while True:
    ld = lead(d)
    for m in r: # iterating through terms
        if is_divisible(m, ld):
            t = m / ld
            q = q + t
            r = r - t * d
            break
    else:
        return [q, r]

You divide until the remainder has no terms that can be divided by the leading term of the divider.

Theoretically you can even do such that you have multiple polynomials you use for division and you divide by them both simultaneously. Such as below:

assert d != 0, "divide by zero"
qs = [0] * len(D)
r = n
while True:
    for i, d in enumerate(D):
        ld = lead(d)
        for m in r:
            if is_divisible(m, ld):
                t = m / ld
                qs[i] = qs[i] + t
                r = r - t * d
                break
        else:
            continue
        break
    else:
        return [qs, r]

This has a problem that the remainder won't be unique in respect to the order of the division. Ordering the D in different ways may give you a different r.

If you read further about this subject, you eventually arrive to the concept of the gröbner basis. The idea there appears to be that if you:

Then this new set produces the same results as the earlier set, except that it gives the same remainder irrespective of which order you take in dividing.

Here's the same in code:

while True:
    for i,j in pairs(G):
        r = reduce(s_polynomial(i,j), G).remainder
        if r != 0:
            break
    else:
        return G
    G.add(r)

I do not yet understand the applications of this all, but it seems to have been important for people who have written computer algebra systems.

Similar posts