NTT Algorithm By Hand

The NTT (Number Theoretic Transform) algorithm converts a polynomial in a finite field from coefficient form to point form.

If a polynomial has degree dd then we evaluate it on the kk-th roots of unity where k>d.k\gt d.

Rather than evaluating the polynomial f(x)f(x) on each point in the set of kk-th roots of unity, {1,ω,ω2,...,ωk1}\set{1,\omega,\omega^2,...,\omega^{k-1}}, we use the image preservation theorem for multivalued functions to evaluate the multivalued function created by substituting xx in ff with xk\sqrt[k]{x} on the domain {1}\set{1}. We then iteratively expand the evaluations of the square root from {1}\set{1} to {1,ωk/2}\set{1,\omega^{k/2}} to {1,ωk/4,ωk/2,ωk/4}\set{1,\omega^{k/4},\omega^{k/2},-\omega^{k/4}} and so on until the evaluation is expanded to the kk-th roots of unity.

The runtime of this method is O(nlogn)\mathcal{O}(n \log n).

Evaluating f(x)=a0+a1x+a2x2+a3x3f(x)=a_0+a_1x+a_2x^2+a_3x^3 on the 4-th roots of unity

First, we factor the function to maximize the occurrences of x2x^2, since 2=k/22=k/2 and xk/2x^{k/2} is easy to evaluate on a root of unity (it only results in {1,1}\set{1,-1} depending on if the power of the root of unity is even or odd).

This creates the following function:

f(x)=a0+a2x2+x(a1+a3x2)f(x)=a_0+a_2x^2+x(a_1+a_3x^2)

Next, we transform ff so that xx4x\rightarrow\sqrt[4]{x} which gives us

f(x)=a0+a2x+x4(a1+a3x)f(x)=a_0+a_2\sqrt{x}+\sqrt[4]{x}(a_1+a_3\sqrt{x})

Here is the square root expansion diagram:

NTT of a degree four polynomial

Now we compare the result to evaluating f(x)f(x) on the 4-th roots of unity one-by-one:

f(1)=a0+a1(1)+a2(1)2+a3(1)3f(1)=a0+a1(1)+a2(1)2+a3(1)3f(ω)=a0+a1(ω)+a2(ω)2+a3(ω)3f(ω)=a0+a1(ω)+a2(ω)2+a3(ω)3\begin{align*} f(1) &= a_0 &+& a_1(1) &+& a_2(1)^2 &+& a_3(1)^3\\ f(-1) &= a_0 &+& a_1(-1) &+& a_2(-1)^2 &+& a_3(-1)^3\\ f(\omega) &= a_0 &+& a_1(\omega) &+& a_2(\omega)^2 &+& a_3(\omega)^3\\ f(-\omega) &= a_0 &+& a_1(-\omega) &+& a_2(-\omega)^2 &+& a_3(-\omega)^3 \end{align*}

We have that ω2=(ω2)=1\omega^2=(-\omega^2)=-1 and ω3=ω\omega^3=-\omega and (ω)3=(1)3(ω)3=ω3=(ω)=ω(-\omega)^3=(-1)^3(\omega)^3=-\omega^3=-(-\omega)=\omega. By substitution, we have:

f(1)=a0+a1+a2+a3f(1)=a0a1+a2a3f(ω)=a0+a1ωa2a3ωf(ω)=a0a1ωa2+a3ω\begin{align*} f(1) &= a_0 &+& a_1 &+& a_2 &+& a_3\\ f(-1) &= a_0 &-& a_1 &+& a_2 &-& a_3\\ f(\omega) &= a_0 &+& a_1\omega &-& a_2 &-& a_3\omega\\ f(-\omega) &= a_0 &-& a_1\omega &-& a_2 &+& a_3\omega \end{align*}

Exercise: Use the above method to evaluate a0+a1x+a2x2a_0+a_1x+a_2x^2 on the 4-th roots of unity. Hint: use the example above and set a3=0a_3=0.

The height of the tree is logn\log n and we do O(n)\mathcal{O}(n) operations on each row, so the runtime is O(nlogn)\mathcal{O}(n \log n).

Evaluating f(x)=a0+a1x+...+a7x7f(x)=a_0+a_1x+...+a_7x^7 on the 8-th roots of unity

First, we rearrange the polynomial to maximize the number of x4x^4 terms (since k = 8). This gives us:

f(x)=a0+a4x4+x2(a2+a6x4)+x((a1+a5x4)+x2(a3+a7x4))f(x)=a_0+a_4x^4+x^2(a_2+a_6x^4)+x((a_1+a_5x^4)+x^2(a_3+a_7x^4))

Now we substitute xx8x\rightarrow\sqrt[8]{x} to get our multivalued function g(x)g(x)

g(x)=a0+a4x+x4(a2+a6x)+x8((a1+a5x)+x4(a3+a7x))g(x)=a_0+a_4\sqrt{x}+\sqrt[4]{x}(a_2+a_6\sqrt{x})+\sqrt[8]{x}((a_1+a_5\sqrt{x})+\sqrt[4]{x}(a_3+a_7\sqrt{x}))

Since drawing the evaluation tree in one image would be quite large, we’ll draw the left side of the tree where we evaluate 1=1\sqrt{1}=1 and show the diagram for that first:

NTT of an 8 degree polynomial only showing the left side of the evaluation tree.

From the image above, we have that

f(1)=((a0+a4)+(a2+a6))+((a1+a5)+(a3+a7))f(1)=((a0+a4)+(a2+a6))+((a1+a5)+(a3+a7))f(ω2)=((a0+a4)(a2+a6))+ω((a1+a5)(a3+a7))f(ω2)=((a0+a4)(a2+a6))ω((a1+a5)(a3+a7))\begin{align*} f(1)=((a_0+a_4)+(a_2+a_6))+((a_1+a_5)+(a_3+a_7))\\ f(-1)=((a_0+a_4)+(a_2+a_6))+((a_1+a_5)+(a_3+a_7))\\ f(\omega^2)=((a_0+a_4)-(a_2+a_6))+\omega((a_1+a_5)-(a_3+a_7))\\ f(-\omega^2)=((a_0+a_4)-(a_2+a_6))-\omega((a_1+a_5)-(a_3+a_7))\\ \end{align*}

Now we expand the right side of the tree where x=1\sqrt{x}=-1:

NTT evaluation of an 8 degree polynomial on the right side of the evaluation tree

From that result, we have that:

f(ω)=((a0a4)+ω2(a2a6))+ω((a1a5)+ω2(a3a7))f(ω)=((a0a4)+ω2(a2a6))ω((a1a5)+ω2(a3a7))f(ω3)=((a0a4)ω2(a2a6))+ω3((a1a5)ω2(a3a7))f(ω3)=((a0a4)ω2(a2a6))ω3((a1a5)ω2(a3a7))\begin{align*} f(\omega)=((a_0-a_4)+\omega^2(a_2-a_6))+\omega((a_1-a_5)+\omega^2(a_3-a_7))\\ f(-\omega)=((a_0-a_4)+\omega^2(a_2-a_6))-\omega((a_1-a_5)+\omega^2(a_3-a_7))\\ f(\omega^3)=((a_0-a_4)-\omega^2(a_2-a_6))+\omega^3((a_1-a_5)-\omega^2(a_3-a_7))\\ f(-\omega^3)=((a_0-a_4)-\omega^2(a_2-a_6))-\omega^3((a_1-a_5)-\omega^2(a_3-a_7)) \end{align*}

Combining the evaluations and distributing the omega terms, we have:

f(1)=a0+a4+a2+a6+a1+a5+a3+a7f(1)=a0+a4+a2+a6a1a5a3a7f(ω2)=a0+a4a2a6+a1ω2+a5ω2a3ω2a7ω2f(ω2)=a0+a4a2a6a1ω2a5ω2+a3ω2+a7ω2f(ω)=a0a4+a2ω2a6ω2+a1ωa5ω+a3ω3a7ω3f(ω)=a0a4+a2ω2a6ω2a1ω+a5ωa3ω3+a7ω3f(ω3)=a0a4a2ω2+a6ω2+a1ω3a5ω3+a3ωa7ωf(ω3)=a0a4a2ω2+a6ω2a1ω3+a5ω3a3ωa7ω\begin{align*} f(1) &= a_0 &+ a_4 &+ a_2 &+ a_6 &+ a_1 &+ a_5 &+ a_3 &+ a_7\\f(-1) &= a_0 &+ a_4 &+ a_2 &+ a_6 &- a_1 &- a_5 &- a_3 &- a_7\\f(\omega^2) &= a_0 &+ a_4 &- a_2 &- a_6 &+ a_1\omega^2 &+ a_5\omega^2 &- a_3\omega^2 &- a_7\omega^2\\f(-\omega^2)&= a_0 &+ a_4 &- a_2 &- a_6 &- a_1\omega^2 &- a_5\omega^2 &+ a_3\omega^2 &+ a_7\omega^2\\f(\omega) &= a_0 &- a_4 &+ a_2\omega^2 &- a_6\omega^2 &+ a_1\omega &- a_5\omega &+ a_3\omega^3 &- a_7\omega^3\\f(-\omega) &= a_0 &- a_4 &+ a_2\omega^2 &- a_6\omega^2 &- a_1\omega &+ a_5\omega &- a_3\omega^3 &+ a_7\omega^3\\f(\omega^3) &= a_0 &- a_4 &- a_2\omega^2 &+ a_6\omega^2 &+ a_1\omega^3 &- a_5\omega^3 &+ a_3\omega &- a_7\omega\\f(-\omega^3)&= a_0 &- a_4 &- a_2\omega^2 &+ a_6\omega^2 &- a_1\omega^3 &+ a_5\omega^3 &- a_3\omega & a_7\omega\end{align*}

Next, we put the coefficients in ascending order:

f(1)=a0+a1+a2+a3+a4+a5+a6+a7f(1)=a0a1+a2a3+a4a5+a6a7f(ω2)=a0+a1ω2a2a3ω2+a4+a5ω2a6a7ω2f(ω2)=a0a1ω2a2+a3ω2+a4a5ω2a6+a7ω2f(ω)=a0+a1ω+a2ω2+a3ω3a4a5ωa6ω2a7ω3f(ω)=a0a1ω+a2ω2a3ω3a4+a5ωa6ω2+a7ω3f(ω3)=a0+a1ω3a2ω2+a3ωa4a5ω3+a6ω2a7ωf(ω3)=a0a1ω3a2ω2a3ωa4+a5ω3+a6ω2+a7ω\begin{align*} f(1) &= a_0 &+ a_1 &+ a_2 &+ a_3 &+ a_4 &+ a_5 &+ a_6 &+ a_7\\ f(-1) &= a_0 &- a_1 &+ a_2 &- a_3 &+ a_4 &- a_5 &+ a_6 &- a_7\\ f(\omega^2) &= a_0 &+ a_1\omega^2 &- a_2 &- a_3\omega^2 &+ a_4 &+ a_5\omega^2 &- a_6 &- a_7\omega^2\\ f(-\omega^2)&= a_0 &- a_1\omega^2 &- a_2 &+ a_3\omega^2 &+ a_4 &- a_5\omega^2 &- a_6 &+ a_7\omega^2\\ f(\omega) &= a_0 &+ a_1\omega &+ a_2\omega^2 &+ a_3\omega^3 &- a_4 &- a_5\omega &- a_6\omega^2 &- a_7\omega^3\\ f(-\omega) &= a_0 &- a_1\omega &+ a_2\omega^2 &- a_3\omega^3 &- a_4 &+ a_5\omega &- a_6\omega^2 &+ a_7\omega^3\\ f(\omega^3) &= a_0 &+ a_1\omega^3 &- a_2\omega^2 &+ a_3\omega &- a_4 &- a_5\omega^3 &+ a_6\omega^2 &- a_7\omega\\ f(-\omega^3)&= a_0 &- a_1\omega^3 &- a_2\omega^2 &- a_3\omega &- a_4 &+ a_5\omega^3 &+ a_6\omega^2 &+ a_7\omega \end{align*}

Now let’s rearrange the evaluations to go from f(1)f(1), f(ω)f(\omega), … , f(ω7)f(\omega^7)

f(1)=a0+a1+a2+a3+a4+a5+a6+a7f(ω)=a0+a1ω+a2ω2+a3ω3a4a5ωa6ω2a7ω3f(ω2)=a0+a1ω2a2a3ω2+a4+a5ω2a6a7ω2f(ω3)=a0+a1ω3a2ω2+a3ωa4a5ω3+a6ω2a7ωf(1)=a0a1+a2a3+a4a5+a6a7f(ω)=a0a1ω+a2ω2a3ω3a4+a5ωa6ω2+a7ω3f(ω2)=a0a1ω2a2+a3ω2+a4a5ω2a6+a7ω2f(ω3)=a0a1ω3a2ω2a3ωa4+a5ω3+a6ω2+a7ω\begin{align*} f(1) &= a_0 &+ a_1 &+ a_2 &+ a_3 &+ a_4 &+ a_5 &+ a_6 &+ a_7\\ f(\omega) &= a_0 &+ a_1\omega &+ a_2\omega^2 &+ a_3\omega^3 &- a_4 &- a_5\omega &- a_6\omega^2 &- a_7\omega^3\\ f(\omega^2) &= a_0 &+ a_1\omega^2 &- a_2 &- a_3\omega^2 &+ a_4 &+ a_5\omega^2 &- a_6 &- a_7\omega^2\\ f(\omega^3) &= a_0 &+ a_1\omega^3 &- a_2\omega^2 &+ a_3\omega &- a_4 &- a_5\omega^3 &+ a_6\omega^2 &- a_7\omega\\ f(-1) &= a_0 &- a_1 &+ a_2 &- a_3 &+ a_4 &- a_5 &+ a_6 &- a_7\\ f(-\omega) &= a_0 &- a_1\omega &+ a_2\omega^2 &- a_3\omega^3 &- a_4 &+ a_5\omega &- a_6\omega^2 &+ a_7\omega^3\\ f(-\omega^2)&= a_0 &- a_1\omega^2 &- a_2 &+ a_3\omega^2 &+ a_4 &- a_5\omega^2 &- a_6 &+ a_7\omega^2\\ f(-\omega^3)&= a_0 &- a_1\omega^3 &- a_2\omega^2 &- a_3\omega &- a_4 &+ a_5\omega^3 &+ a_6\omega^2 &+ a_7\omega \end{align*}

If we compare this to the Vandermonde matrix for the 8-th roots of unity, we can see we correctly computed the powers of ω\omega.

V=[111111111ωω2ω31ωω2ω31ω21ω21ω21ω21ω3ω2ω1ω3ω2ω111111111ωω2ω31ωω2ω31ω21ω21ω21ω21ω3ω2ω1ω3ω2ω]\mathbf{V} =\begin{bmatrix} 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 \\ 1 & \omega & \omega^{2} & \omega^{3} & -1 & -\omega & -\omega^{2} & -\omega^{3} \\ 1 & \omega^{2} & -1 & -\omega^{2} & 1 & \omega^{2} & -1 & -\omega^{2} \\ 1 & \omega^{3} & -\omega^{2} & \omega & -1 & -\omega^{3} & \omega^{2} & -\omega \\ 1 & -1 & 1 & -1 & 1 & -1 & 1 & -1 \\ 1 & -\omega & \omega^{2} & -\omega^{3} & -1 & \omega & -\omega^{2} & \omega^{3} \\ 1 & -\omega^{2} & -1 & \omega^{2} & 1 & -\omega^{2} & -1 & \omega^{2} \\ 1 & -\omega^{3} & -\omega^{2} & -\omega & -1 & \omega^{3} & \omega^{2} & \omega \end{bmatrix}

Vandermonde matrix computation

The Vandermonde matrix above was derived as follows. Each row is the powers of xx for f(x)=a0+a1x+...+a7x7f(x)=a_0+a_1x+...+a_7x^7 on x={1,ω,ω2,...,ω7}x=\set{1,\omega,\omega^2,...,\omega^7}

V=[111111111ωω2ω3ω4ω5ω6ω71ω2ω4ω6ω8ω10ω12ω141ω3ω6ω9ω12ω15ω18ω211ω4ω8ω12ω16ω20ω24ω281ω5ω10ω15ω20ω25ω30ω351ω6ω12ω18ω24ω30ω36ω421ω7ω14ω21ω28ω35ω42ω49]\mathbf{V}= \begin{bmatrix} 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1\\ 1 & \omega & \omega^{2} & \omega^{3} & \omega^{4} & \omega^{5} & \omega^{6} & \omega^{7}\\ 1 & \omega^{2} & \omega^{4} & \omega^{6} & \omega^{8} & \omega^{10} & \omega^{12} & \omega^{14}\\ 1 & \omega^{3} & \omega^{6} & \omega^{9} & \omega^{12} & \omega^{15} & \omega^{18} & \omega^{21}\\ 1 & \omega^{4} & \omega^{8} & \omega^{12} & \omega^{16} & \omega^{20} & \omega^{24} & \omega^{28}\\ 1 & \omega^{5} & \omega^{10} & \omega^{15} & \omega^{20} & \omega^{25} & \omega^{30} & \omega^{35}\\ 1 & \omega^{6} & \omega^{12} & \omega^{18} & \omega^{24} & \omega^{30} & \omega^{36} & \omega^{42}\\ 1 & \omega^{7} & \omega^{14} & \omega^{21} & \omega^{28} & \omega^{35} & \omega^{42} & \omega^{49} \end{bmatrix}

Next, we factor out multiples of 8:

V=[111111111ωω2ω3ω4ω5ω6ω71ω2ω4ω6ω8ω8ω2ω8ω4ω8ω61ω3ω6ω8ωω8ω4ω8ω7ω16ω2ω16ω51ω4ω8ω8ω4ω16ω16ω4ω24ω24ω41ω5ω8ω2ω8ω7ω16ω4ω24ωω24ω6ω32ω31ω6ω8ω4ω16ω2ω24ω24ω6ω32ω4ω40ω21ω7ω8ω6ω16ω5ω24ω4ω32ω3ω40ω2ω48ω]\mathbf{V}= \begin{bmatrix} 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1\\ 1 & \omega & \omega^{2} & \omega^{3} & \omega^{4} & \omega^{5} & \omega^{6} & \omega^{7}\\ 1 & \omega^{2} & \omega^{4} & \omega^{6} & \omega^{8} & \omega^{8}\omega^{2} & \omega^{8}\omega^{4} & \omega^{8}\omega^{6}\\ 1 & \omega^{3} & \omega^{6} & \omega^{8}\omega & \omega^{8}\omega^{4} & \omega^{8}\omega^{7} & \omega^{16}\omega^{2} & \omega^{16}\omega^{5}\\ 1 & \omega^{4} & \omega^{8} & \omega^{8}\omega^{4} & \omega^{16} & \omega^{16}\omega^{4} & \omega^{24} & \omega^{24}\omega^{4}\\ 1 & \omega^{5} & \omega^{8}\omega^{2} & \omega^{8}\omega^{7} & \omega^{16}\omega^{4} & \omega^{24}\omega & \omega^{24}\omega^{6} & \omega^{32}\omega^{3}\\ 1 & \omega^{6} & \omega^{8}\omega^{4} & \omega^{16}\omega^{2} & \omega^{24} & \omega^{24}\omega^{6} & \omega^{32}\omega^{4} & \omega^{40}\omega^{2}\\ 1 & \omega^{7} & \omega^{8}\omega^{6} & \omega^{16}\omega^{5} & \omega^{24}\omega^{4} & \omega^{32}\omega^{3} & \omega^{40}\omega^{2} & \omega^{48}\omega\\ \end{bmatrix}

Removing the factors of 8 we have:

V=[111111111ωω2ω3ω4ω5ω6ω71ω2ω4ω61ω2ω4ω61ω3ω6ωω4ω7ω2ω51ω41ω41ω41ω41ω5ω2ω7ω4ωω6ω31ω6ω4ω21ω6ω4ω21ω7ω6ω5ω4ω3ω2ω]\mathbf{V} = \begin{bmatrix} 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1\\ 1 & \omega & \omega^{2} & \omega^{3} & \omega^{4} & \omega^{5} & \omega^{6} & \omega^{7}\\ 1 & \omega^{2} & \omega^{4} & \omega^{6} & 1 & \omega^{2} & \omega^{4} & \omega^{6}\\ 1 & \omega^{3} & \omega^{6} & \omega & \omega^{4} & \omega^{7} & \omega^{2} & \omega^{5}\\ 1 & \omega^{4} & 1 & \omega^{4} & 1 & \omega^{4} & 1 & \omega^{4}\\ 1 & \omega^{5} & \omega^{2} & \omega^{7} & \omega^{4} & \omega & \omega^{6} & \omega^{3}\\ 1 & \omega^{6} & \omega^{4} & \omega^{2} & 1 & \omega^{6} & \omega^{4} & \omega^{2}\\ 1 & \omega^{7} & \omega^{6} & \omega^{5} & \omega^{4} & \omega^{3} & \omega^{2} & \omega \end{bmatrix}

After replacing ω4=1\omega^4=-1, ω5=ω\omega^5=-\omega, ω6=ω2\omega^6=-\omega^2, ω7=ω3\omega^7=-\omega^3 we have the original Vandermonde matrix:

V=[111111111ωω2ω31ωω2ω31ω21ω21ω21ω21ω3ω2ω1ω3ω2ω111111111ωω2ω31ωω2ω31ω21ω21ω21ω21ω3ω2ω1ω3ω2ω]\mathbf{V} =\begin{bmatrix} 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 \\ 1 & \omega & \omega^{2} & \omega^{3} & -1 & -\omega & -\omega^{2} & -\omega^{3} \\ 1 & \omega^{2} & -1 & -\omega^{2} & 1 & \omega^{2} & -1 & -\omega^{2} \\ 1 & \omega^{3} & -\omega^{2} & \omega & -1 & -\omega^{3} & \omega^{2} & -\omega \\ 1 & -1 & 1 & -1 & 1 & -1 & 1 & -1 \\ 1 & -\omega & \omega^{2} & -\omega^{3} & -1 & \omega & -\omega^{2} & \omega^{3} \\ 1 & -\omega^{2} & -1 & \omega^{2} & 1 & -\omega^{2} & -1 & \omega^{2} \\ 1 & -\omega^{3} & -\omega^{2} & -\omega & -1 & \omega^{3} & \omega^{2} & \omega \end{bmatrix}

Exercise: Evaluate f(x)=a0+a1x+a2x2+a3x3+a4x4+a5x5+a6x6f(x)=a_0+a_1x+a_2x^2+a_3x^3+a_4x^4+a_5x^5+a_6x^6 on the 8-th roots of unity. Again, note that you can set a7=0a_7=0.

Exercise: Evaluate f(x)=3+2x+9x2+x3f(x)=3 +2x+9x^2+x^3 on the 4-th roots of unity in Fq\mathbb{F}_q where q=17q=17. Use Python to find a primitive 4-th root of unity as a starting point.

Summary

Evaluating a polynomial on the kk-th roots of unity using square root expansion has the same evaluations as evaluating the polynomial on the roots of unity one at a time. This holds due to the Image Preservation Theorem for Multivalued Functions as we are simply evaluating the multivalued function on the domain {1}\set{1}.

This method saves computation cost because at each step, half of the square roots are evaluated and multiplied the coefficient or sum of coefficients they are paired with. For the remaining evaluations, the results are simply copied down instead of being reevaluated.

Ready to Get Started?Join Thousands of Users Today

Start your free trial now and experience the difference. No credit card required.

© 2025 Better-Start. All rights reserved.