Complex numbers are not complicated.
They just need a less alarming name.
They are so useful, especially in engineering and science, that R includes complex numbers as standard atomic types alongside integer, double, logical, character and raw.
A complex number in R is a single value.
In use, however, it can be thought of as a pair of numbers.
These are called the "real" and "imaginary" parts, for unfortunate historical reasons.
Again, it is best to focus on the underlying simplicity and not the strange names.
To create complex numbers from two real numbers, just add the suffix i to the imaginary part.
2+3i
#> [1] 2+3i
typeof(2+3i)
#> [1] "complex"
To create a complex number from real variables, the above syntax will not work.
Writing r + imi confuses the parser into thinking imi is a (non-existent) variable name.
Either multiply the imaginary part by 1i or use the [complex()][ref-complex] function:
r <- 2
im <- 3
r + im * 1i
#> [1] 2+3i
# must include the `real` and `imaginary` keywords
complex(real = r, imaginary = im)
#> [1] 2+3i
You may wonder, why i rather than j?
Most engineers prefer j.
Most scientists and mathematicians prefer the mathematical notation i for imaginary, but that notation conflicts
with the use of i to mean current in Electrical Engineering.
R chose i, Python chose j, and Julia sidestepped the controversy by choosing im.
Be careful when switching between [STEM][wiki-STEM] programming languages.
To access the parts of a complex number individually:
z <- 2+3i
Re(z) # real part
#> [1] 2
Im(z) # imaginary part
#> [1] 3
Either part can be zero and mathematicians may then talk of the number being "wholly real" or "wholly imaginary". However, it is still a complex number in R.
1.2 + 0i
#> [1] 1.2+0i
typeof(1.2 + 0i)
#> [1] "complex"
2i
#> [1] 0+2i
typeof(2i)
#> [1] "complex"
You may have heard that "i (or j) is the square root of -1".
For now, all this means is that the imaginary part by definition satisfies the following equality:
1i * 1i == -1 # more precisely, 1i * 1i == -1 + 0i
#> [1] TRUE
This is a simple idea, but it leads to interesting consequences.
Most of the operators used with floats and integers also work with complex numbers:
z1 = 1.5 + 2i
z2 = 2 + 1.5i
z1 + z2 # addition
#> [1] 3.5+3.5i
z1 * z2 # multiplication
#> [1] 0+6.25i
z1 / z2 # division
#> [1] 0.96+0.28i
z1^2 # exponentiation
#> [1] -1.75+6i
2^z1 # another exponentiation
#> [1] 0.518895+2.780422i
Most mathematical functions will work with complex inputs.
There are several functions, in addition to Re() and Im(), with particular relevance for complex numbers.
Conj() simply flips the sign of the imaginary part of a complex number (from + to - or vice-versa).
abs(<complex number>) is guaranteed to return a real number with no imaginary part (its "magnitude"), and Mod(<complex number>) is a synonym.Arg(<complex number>) returns the phase angle in radians.z1
#> [1] 1.5+2i
abs(z1)
#> [1] 2.5
Mod(z1) # same as previous
#> [1] 2.5
Conj(z1) # flip sign
#> [1] 1.5-2i
Arg(z1)
#> [1] 0.9272952
You have just been hired at a new cab service which is trying to differentiate itself from other offerings in an already saturated market. To achieve this, the CEO has decided to offer an ultra-luxury service in Manhattan with attendants at every intersection to assist customers before and after their ride. Now, you need to write some code that will help with the operations of the service.
Your CEO has decided that since Manhattan has a mainly grid-like street layout, complex numbers are a convenient way to represent the intersections of the streets. This has positive and negative real numbers representing east and west, and positive and negative imaginary numbers representing north and south, respectively.
Since instantiating a complex number in R is a bit, er... unique, you have been instructed to implement a simple complex number function.
Implement the simplex(a, b) function, which takes integers a and b.
Return the complex number a+bi.
simplex(1, 1)
#> 1+1i
simplex(-3, 4)
#> -3+4i
When a customer gives the destination intersection, your drivers will get directions for how to get there. These directions come in the form of a complex number with the real part representing how many blocks north/south to go and the imaginary part representing the east/west distance. To align with a canonical compass, your project manager has decided north will be in the positive imaginary direction, and south is in the negative imaginary direction. Likewise, east is in the positive real direction, and west is in the negative real direction.
Implement the driver_directions(start, end) function, which takes the starting intersection coordinates and the end destination coordinates.
Return a complex number representing how many blocks east/west + north/south the driver has to go.
driver_directions(0+0i, 7+5i)
#> 7+5i
driver_directions(-2+3i, 4-5i)
#> 6-8i
The customer is charged a fee for the distance that the cab has to cover. Since Manhattan has a grid-like street layout, the taxis can only travel along these routes. This structure leads to a distance measure constrained to follow the grid lines. The distance can be found by adding up either the number of lines traversed or the number of intersections reached (i.e. not including the starting intersection). This measure is commonly called the Manhattan distance or L1 norm.
Implement the manhattan(start, end) function, which takes the start and end intersections.
Return the Manhattan distance between the start and end intersections.
manhattan(0+0i, 3+4i)
#> 7
manhattan(-2-3i, -1+1i)
#> 5
The CEO was able to get investment banks onboard by highlighting that the service is not only ultra-luxury but green, because it will use trained crows to ferry messages between the attendants at the intersections (which relay information about the passengers for a personalized experience). It appears none of the investment bankers bothered to do their due diligence on finding out why exactly this is environmentally friendly, or even what kind of CEO enjoys training crows in their free time, because the investment money has been rolling in.
This service is also charged by distance traveled. However, since crows can fly, they don't have to follow the grid network of roads and can take the shortest path. Of course, this might not be the case when there are huge skyscrapers, but the CEO is putting this service in Manhattan, Kansas (aka "The Little Apple"), where the crows can easily fly over the low-rise buildings. The potential demand for such a service in Manhattan, Kansas, and how the local pedestrians will feel about all these birds flying overhead are matters for another day.
Implement the as_crow_flies(start, end) function, which takes the start and end intersections.
Return the as-the-crow-flies distance between the two.
as_crow_flies(0+0i, 3+4i)
#> 5
Your CEO has trained the crows to follow similar kinds of directions to the drivers. However, the CEO has a love of the Southern Hemisphere and trained the crows using a different compass from that used by your project manager for the rest of the system. Your CEO is turning out to be quite an eccentric person. The CEO's compass has north being in the negative imaginary direction, and south being the positive imaginary direction. This is the opposite to the drivers' north/south directions, while east/west remain the same.
Implement the crows_directions(start, end) function, which takes the start and end intersections.
Return the complex number analogous to the driver's directions, but with north/south flipped.
driver_directions(0+0i, 7+5i)
#> 7-5i
driver_directions(-2+3i, 4-5i)
#> 6+8i
Sign up to Exercism to learn and master R with 21 concepts111 exercises, and real human mentoring, all for free.