Justus Perlwitz has a nice interactive post which calculates (and plots) the nth roots of unity: www.justus.pw/garden/nt… and it made me want to do something similar in R which is well known for its statistical capabilities.

The formula Justus shows for the complex roots of z^n = 1 is

z=cis(2*k*pi/n) for 0 <= k < n

and despite R having great support for complex numbers (as I explored previously) and trigonometry functions, there’s no specific cis() so I’ll have to define one (easily enough)

cis <- function(theta) cos(theta) + 1i * sin(theta)

I can then calculate the roots by evaluating this function with the arguments 2*pi*(0:(n-1))/n thanks in part to R’s vectorisation

roots <- function(n) cis(2*(0:(n-1))*pi/n)

Just to make this look a bit nicer, I can also use zapsmall() to round any tiny values towards 0, and sort() the results (first by the real part, then the imaginary part, by default for complex numbers)

roots <- function(n) sort(zapsmall(cis(2*(0:(n-1))*pi/n)))

Of course, there’s ways to actually solve the equation z^n = 1 in R… If I set up a polynomial

p(x) = z1 + z2*x + z3*x^2 + ... + zn*x^{n-1}

then rearrange it to

-1 + ... + zn*x^{n-1} = 0

then that’s equivalent to z^n = 1 and can be solved. I just need the coefficients of the left side, which are -1, n-1 lots of 0, and a 1, i.e.

c(-1, rep(0, n-1), 1)

I can pass that to the base function polyroot() and it will return the complex roots. Again, I’ll use zapsmall() and sort() for the sake of comparison

solver <- function(n) sort(zapsmall(polyroot(c(-1, rep(0, n-1), 1))))

One last option is to leverage the fact that while R doesn’t have a cis(), Euler’s formula says that it’s equivalent to an exponential, exp(ix) = cos(x) + i sin(x) so I could just use that

euler <- function(n) sort(zapsmall(exp((2*pi*1i*(0:(n-1)))/n)))

So, does it work? You bet

roots(2)
#> [1] -1+0i  1+0i
solver(2)
#> [1] -1+0i  1+0i
euler(2)
#> [1] -1+0i  1+0i

roots(3)
#> [1] -0.5-0.8660254i -0.5+0.8660254i  1.0+0.0000000i
solver(3)
#> [1] -0.5-0.8660254i -0.5+0.8660254i  1.0+0.0000000i
euler(3)
#> [1] -0.5-0.8660254i -0.5+0.8660254i  1.0+0.0000000i

roots(4)
#> [1] -1+0i  0-1i  0+1i  1+0i
solver(4)
#> [1] -1+0i  0-1i  0+1i  1+0i
euler(4)
#> [1] -1+0i  0-1i  0+1i  1+0i

roots(5)
#> [1] -0.809017-0.5877853i -0.809017+0.5877853i  0.309017-0.9510565i
#> [4]  0.309017+0.9510565i  1.000000+0.0000000i
solver(5)
#> [1] -0.809017-0.5877853i -0.809017+0.5877853i  0.309017-0.9510565i
#> [4]  0.309017+0.9510565i  1.000000+0.0000000i
euler(5)
#> [1] -0.809017-0.5877853i -0.809017+0.5877853i  0.309017-0.9510565i
#> [4]  0.309017+0.9510565i  1.000000+0.0000000i

I was also tempted to try this out in Julia, which does have a cis() built-in and also deals well with complex numbers

nroots(n) = round.([cis(2π * k / n) for k in 0:(n-1)]; digits=3)
nroots (generic function with 1 method)

nroots(4)
4-element Vector{ComplexF64}:
  1.0 + 0.0im
  0.0 + 1.0im
 -1.0 + 0.0im
 -0.0 - 1.0im

nroots(5)
5-element Vector{ComplexF64}:
    1.0 + 0.0im
  0.309 + 0.951im
 -0.809 + 0.588im
 -0.809 - 0.588im
  0.309 - 0.951im

Simply beautiful, isn’t it?