Skip to content

Adding polynomials

Adding two sparse polynomials is a merge of their term lists by exponent, combining like terms and freeing those that cancel.

Helps to know first
This topic

First, the picture

Two polynomials as chains of terms, largest exponent first. Watch them merge into one chain, with two pairs of terms cancelling out.

Adding polynomials term by term

Two sorted chains of terms merged, with cancelling pairs removed

struct pnode *poly_add(struct pnode *a, struct pnode *b){    struct pnode dummy;    struct pnode *tail = &dummy;    while (a != NULL && b != NULL) {        if (a->exp > b->exp) {            tail->next = a;            tail = a;            a = a->next;        } else if (b->exp > a->exp) {            tail->next = b;            tail = b;            b = b->next;        } else {            struct pnode *na = a->next;            struct pnode *nb = b->next;            a->coeff += b->coeff;            free(b);            if (a->coeff == 0.0) {                free(a);            } else {                tail->next = a;                tail = a;            }            a = na;            b = nb;        }    }    tail->next = (a != NULL) ? a : b;    return dummy.next;}int main(void){    const struct term ta[4] = { { 5, 4 }, { 3, 2 }, { -2, 1 }, { 1, 0 } };    const struct term tb[4] = { { 2, 3 }, { -3, 2 }, { 4, 1 }, { -1, 0 } };    struct pnode *a = from_terms(ta, 4);    struct pnode *b = from_terms(tb, 4);    print_poly("A", a);    print_poly("B", b);    struct pnode *s = poly_add(a, b);    print_poly("A + B", s);    free_poly(s);    return 0;}

A = 5x⁴ + 3x² − 2x + 1 as terms, largest exponent first.

rounds
0
nodes freed
0

Output

0.00 / 15.60 s
Describe this picture

The picture is three rows, each a head with a chain of nodes to its right, joined by arrows: A, what is left of the first list; B, what is left of the second; and A + B, the sum built so far. Each node shows one term, as 5x^4. Over the rows is a lane with the current round. Under the picture are the lines of polyadd.c the clip runs, with the line about to run marked, one sentence on the current step, two readouts (the rounds of the merge and the nodes freed), and the output. The clip plays once, about 16 seconds. First the two lists are built, A = 5x⁴ + 3x² − 2x + 1 and B = 2x³ − 3x² + 4x − 1, and printed. Then each round lights the two front terms. Round 1 compares exponents 4 and 3, and 5x⁴ slides from A’s row into the sum; round 2 moves 2x³ from B. In round 3 both fronts are x² terms: A’s becomes 0x², toned as a warning, B’s is freed, and then A’s is freed too, so the pair is gone. Round 4 adds −2x and 4x: A’s node becomes 2x¹, B’s is freed, and 2x¹ joins the sum. Round 5 cancels 1 and −1 the same way as round 3. Both rows are now empty, the readouts show 5 rounds and 5 nodes freed, and the clip rests on the output A + B: +5x^4 +2x^3 +2x^1, with three nodes left of eight. Watch only: there is no control. The transport plays and pauses, steps one beat back or forward, and scrubs; a paused step is kept in the page address as add.step.

A merge by exponent

A = 5x⁴ + 3x² − 2x + 1 and B = 2x³ − 3x² + 4x − 1 are stored as lists of term nodes, as in storing a polynomial, largest exponent first:

A: +5x^4 +3x^2 -2x^1 +1x^0
B: +2x^3 -3x^2 +4x^1 -1x^0

Adding them is the merge of merging lists, ordered by falling exponent, with a third case: when the two exponents are equal, the terms are like terms and their coefficients add.

The code and its cost

The merge

poly_add builds the sum on a node dummy on the stack, with tail on the last node so far. While both lists have terms, the term with the larger exponent goes next:

if (a->exp > b->exp) {
    tail->next = a;
    tail = a;
    a = a->next;
} else if (b->exp > a->exp) {
    tail->next = b;
    tail = b;
    b = b->next;
}

No new node is made: the sum reuses the nodes of A and B, so after the call a and b must not be used again.

Equal exponents

Otherwise both nodes hold the same power of x:

struct pnode *na = a->next;
struct pnode *nb = b->next;
a->coeff += b->coeff;
free(b);
if (a->coeff == 0.0) {
    free(a);
} else {
    tail->next = a;
    tail = a;
}
a = na;
b = nb;

A’s node takes the sum and B’s node is freed. If the sum is 0, A’s node is freed too, and nothing is linked. Keeping a term with coefficient 0 would not change the polynomial’s value, but the list would stop being the polynomial’s terms, and every later sum or product would walk past it. na and nb keep the next nodes, since a freed node’s next must not be read.

The coefficients here are small whole numbers, so a->coeff == 0.0 is exact. With measured coefficients, a program treats a sum within a small tolerance of 0 as 0.

The rounds

RoundExponentsWhat happens
14 and 35x⁴ is linked
22 and 32x³ is linked
32 and 23 − 3 = 0: both nodes freed
41 and 1−2 + 4 = 2: A’s node kept, B’s freed
50 and 01 − 1 = 0: both nodes freed

Both lists are then empty, so tail->next = (a != NULL) ? a : b; attaches NULL, and the sum is

A + B: +5x^4 +2x^3 +2x^1

Three nodes of eight are left; five were freed.

Why the lists must be sorted

Like terms meet only because both lists are in the same order. If B’s x² term came after its x term, the merge would pass A’s 3x² before seeing B’s −3x², and the sum would hold two x² terms.

The cost

Each round moves past at least one term, and the loop stops while one list still has a term, unless the last round used up both at once. So lists of m and n terms take at most m + n − 1 rounds, and no new nodes. A version that must keep A and B intact would allocate a new node for each term of the sum instead, and leave the inputs alone.

Running it yourself

gcc -std=c11 -Wall -Wextra -pedantic polyadd.c -o polyadd
./polyadd

Worked example

One node left. (x² + 1) + (−x² + 1): the x² terms cancel, and the constants make 2. The sum is the single node 2x⁰.

A polynomial plus its negative. Every pair cancels, every node is freed, and poly_add returns NULL: the zero polynomial, an empty list.

No common exponents. Adding 3 terms to 5 terms with no exponent in common takes at most 3 + 5 − 1 = 7 rounds, when the exponents interleave. If all of A’s exponents are larger than B’s, it takes 3: once A is used up, the rest of B is attached in one step.

Two checks. Which node of A survives as 2x? (A’s −2x node, its coefficient changed to 2.) What does poly_add return for two empty lists? (NULL.)

Where it’s used

Computer algebra. Adding polynomials with many terms is the basic step of symbolic computation.

Error-correcting codes. Decoders add and combine polynomials whose coefficients are bits or bytes.

Filters. Two filters in parallel have the sum of their transfer functions as theirs, and that sum is worked out with polynomial products and additions.

Reference card

Code or ideaMeaning
larger exponent firstthe merge order of two sorted term lists
a->coeff += b->coeff; free(b);like terms: A’s node keeps the sum
if (a->coeff == 0.0) free(a);a cancelling pair leaves nothing
na, nbthe next nodes, saved before freeing
m + n − 1 roundsat most, with no new nodes

Next, multiplying polynomials takes every term times every term.

Platform: C11, gcc on x86-64 Linux: int 4 bytes, double 8, pointers 8, little-endian. Addresses in the pictures are short stand-ins, by one rule for the whole course; sizes are the real ones.

End of the lesson

Where to go next.

New lessons by email

Are you 13 or over?

Accounts and email updates are for people aged 13 or over. We keep only your answer, never your date of birth.

Phasorium

Account

Sign inWith Google, GitHub or your email

Subjects

Site

Look