Skip to content

Multiplying polynomials

Every term of one polynomial multiplies every term of the other; for dense coefficients the result is the convolution of the two coefficient arrays.

Helps to know first
This topic

First, the picture

(1 + 2x + 3x²)(4 + 5x): three terms times two terms. Watch the six products fill a grid and collect into four coefficients.

Multiplying two polynomials

Every term times every term, collected by power

static int products = 0; void poly_mul(const int *a, int na, const int *b, int nb, int *c){    for (int k = 0; k < na + nb - 1; k++)        c[k] = 0;    for (int i = 0; i < na; i++) {        for (int j = 0; j < nb; j++) {            c[i + j] += a[i] * b[j];            products++;        }    }} int main(void){    const int a[3] = { 1, 2, 3 };    const int b[2] = { 4, 5 };    int c[4];    poly_mul(a, 3, b, 2, c);    printf("c:");    for (int k = 0; k < 4; k++)        printf(" %d", c[k]);    printf(", %d products\n", products);    return 0;}

a = 1 + 2x + 3x²: a[i] is the coefficient of xⁱ.

products
0

Output

0.00 / 15.60 s
Describe this picture

The picture is a grid of 3 rows and 2 columns: the rows are labelled with the terms of a, 1, 2x and 3x², and the columns with the terms of b, 4 and 5x. Under it is a strip of 4 boxes, c, the coefficient of xᵏ in box k. Each grid cell is tied to the box of its power: the cells of one anti-diagonal, running from upper right to lower left, share a power and a box. Under the grid is a line of text with the current product or sum. Under the picture are the program poly-mul.c, with the line about to run marked, one sentence on the current step, a readout “products”, and the output. The clip plays once, about 16 seconds. The strip is cleared to 0 0 0 0. Then the six products are made row by row, and each fills its cell and flies down into its box: 1 × 4 = 4 into c[0], 1 × 5x = 5x and 2x × 4 = 8x into c[1], 2x × 5x = 10x² and 3x² × 4 = 12x² into c[2], and 3x² × 5x = 15x³ into c[3]. The strip ends at 4, 13, 22 and 15. While the program prints c, each anti-diagonal lights in turn with its sum: c[1] = 5 + 8 = 13 and c[2] = 10 + 12 = 22. The clip rests on the output c: 4 13 22 15, 6 products. 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 multiply.step.

Every term times every term

To multiply two polynomials, multiply every term of one by every term of the other and collect the products by power. For a = 1 + 2x + 3x² and b = 4 + 5x, whose terms are 4 and 5x:

Term of aFirst term of bSecond term of b
145x
2x8x10x²
3x²12x²15x³

Six products, but only four powers: the two x terms add to 13x, and the two x² terms to 22x². The product is 4 + 13x + 22x² + 15x³. It does not have m · n terms: like terms collect.

The code and its cost

The double loop

With dense coefficient arrays, term i of a times term j of b lands on power i + j:

void poly_mul(const int *a, int na, const int *b, int nb, int *c)
{
    for (int k = 0; k < na + nb - 1; k++)
        c[k] = 0;
    for (int i = 0; i < na; i++) {
        for (int j = 0; j < nb; j++) {
            c[i + j] += a[i] * b[j];
            products++;
        }
    }
}

The product of a degree-(na − 1) and a degree-(nb − 1) polynomial has degree na + nb − 2, so na + nb − 1 coefficients. The program prints

c: 4 13 22 15, 6 products

m terms times n terms take m · n products.

A convolution

Collect the products by power instead of by row: coefficient k gathers every a[i] · b[j] with i + j = k, that is

c[k] = Σᵢ a[i] · b[k − i],

over the i for which both indices are in range. That is the convolution of the two coefficient arrays: multiplying polynomials and filtering a signal are the same computation. In the grid, each sum runs along an anti-diagonal. The Toeplitz matrices lesson writes the same sum as a matrix product.

Sparse polynomials

For polynomials stored as lists of terms, multiply b by one term of a at a time, and add the partial products with the merge of adding polynomials:

struct pnode *poly_mul(const struct pnode *a, const struct pnode *b)
{
    struct pnode *result = NULL;
    for (const struct pnode *p = a; p != NULL; p = p->next)
        result = poly_add(result, times_term(b, p->coeff, p->exp));
    return result;
}

times_term builds a new list, b’s terms each multiplied by one term: the coefficients multiply and the exponents add. The program poly-mul-sparse.c multiplies (x¹⁰⁰ + 1)(x¹⁰⁰ − 1):

product: +1x^200 -1x^0

That takes 4 products, and the two x¹⁰⁰ terms cancel inside poly_add. With dense arrays of 101 coefficients each, the double loop would make 101 × 101 = 10,201 products, nearly all of them 0.

Faster methods

For dense polynomials of n coefficients the double loop takes n² products. Two faster methods exist. Split each polynomial into a low half and a high half; the product needs the three half-size products lo × lo, hi × hi and (lo + hi) × (lo + hi), instead of four, and repeating the split down to single terms takes about n^1.585 products. This is Karatsuba’s method, a later lesson. Or evaluate both polynomials with the FFT at as many points as the product has coefficients, 2n − 1, rounded up to a power of two in practice, multiply the values, and convert back, about n log n steps: fast convolution.

Running it yourself

gcc -std=c11 -Wall -Wextra -pedantic poly-mul.c -o poly-mul
./poly-mul
gcc -std=c11 -Wall -Wextra -pedantic poly-mul-sparse.c -o poly-mul-sparse
./poly-mul-sparse

Worked example

A square. (1 + x + x² + x³)² makes 4 × 4 = 16 products. The anti-diagonals of the 4 × 4 grid have 1, 2, 3, 4, 3, 2 and 1 cells, so the coefficients are 1 2 3 4 3 2 1.

One coefficient. The coefficient of x² in the program’s product: a[0] · b[2] + a[1] · b[1] + a[2] · b[0] = 1 · 0 + 2 · 5 + 3 · 4 = 22, since b has no x² term.

Large polynomials. Two polynomials of degree 1,023 have 1,024 coefficients each: 1,024² = 1,048,576 products by the double loop. Splitting in halves ten times, down to single terms, takes 3¹⁰ = 59,049 products.

Two checks. How many coefficients does a degree-4 by degree-2 product have? (7.) How many products for 5 terms by 3 terms? (15.)

Where it’s used

Computer algebra. Multiplying symbolic polynomials, with term lists for sparse ones.

Large integers. A number’s digits are the coefficients of a polynomial evaluated at x = 10; multiplying very large integers is multiplying polynomials and carrying, with the fast methods for numbers of thousands of digits.

Filtering. Filtering a signal is the same convolution (convolution), done fast with the FFT for long filters (fast convolution).

Reference card

Code or ideaMeaning
c[i + j] += a[i] * b[j]term i times term j lands on power i + j
m · nproducts for m terms times n terms
na + nb − 1coefficients of the dense product
c[k] = Σ a[i] · b[k − i]the convolution of the coefficient arrays
poly_add(result, times_term(b, …))sparse: one partial product per term of a
about n^1.585, about n log nthree-product split; FFT

Back to adding polynomials.

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