How to Solve an Equation in R

In this article, will use the uniroot.all() function from the rootSolve package to find all the solutions of an equation over a given interval (or domain).

Input: uniroot.all() takes 2 arguments: a function f and an interval.

How it works: Its searches the interval for all possible roots of f.

Output: uniroot.all() returns a vector of all roots of f over the interval.

Here are some examples:

1. Find the solution of a simple function: \(|x| + 4 = 0\)

First we enter the function in R:

library(rootSolve)

# writing the function
f1 = function(x) {
  abs(x) - 4
}

Next, we plot the function to try to determine visually how many solutions it has (i.e. where and how many times it crosses the line y = 0). This will help us define an interval (domain) over which we will search for a solution.

# let's try plotting the function
# over the range [-50, 50]
x = -50:50
plot(x, f1(x), type = 'l')

# adding a horizontal line at y = 0
abline(h = 0, col = 'blue')

Output:

Graph of the function f1

So we see that the function crosses the blue line at 2 points, and that the domain [-50, 50] covers all the solutions of the function f.

Next, we will use uniroot.all() to find all the solutions of f over [-50, 50]

roots = uniroot.all(f1, c(-50, 50))

# print(roots) outputs: -4  4

# graphing the roots of f1
points(x = roots, y = rep(0, length(roots)), col = "red", pch = 16, cex = 1.5)

Output:

graphing the roots of f1

2. Find the solution of a more complex function: \(\frac{\sqrt{x}}{(x + 2)} – \frac{1}{4} = 0\)

library(rootSolve)

# writing the function
f2 = function(x) {
  sqrt(x) / (x + 2) - 1/4
}

Next, we will plot the function over the interval [0, 100] (since it contains \(\sqrt{x}\), it doesn’t make sense to try negative values).

# let's try plotting the function
# over the range [0, 100]
x = 0:100
plot(x, f2(x), type = 'l')

# adding a horizontal line at y = 0
abline(h = 0, col = 'blue')

Output:

graph of the function f2

We see that the function f has 2 solutions in the domain [0,100].

Let’s find exactly what they are:

uniroot.all(f2, c(0, 100))

# outputs: 0.3431545 11.6568502

# let's plot the solutions
points(x = roots, y = rep(0, length(roots)), col = "red", pch = 16, cex = 1.5)

Output:

graphing the roots of f2

3. Find the solution of the function: \(\frac{1}{x} = 0\)

This example shows that uniroot.all() has some imperfections. Sometimes it cannot find all the roots over the specified interval, and sometimes it even outputs a wrong answer.

library(rootSolve)

# writing the function
f3 = function(x) {
  1 / x
}

Plotting the function over the domain [-50, 50].

x = -50:50
plot(x, f3(x), type = 'l')
abline(h = 0, col = 'blue')

Output:

graph of the function f3

We see that \(\frac{1}{x} = 0\) does not have any solutions.

Anyway, let’s run uniroot.all() on f3:

# finding the roots of f3
roots = uniroot.all(f3, c(-50, 50))

# print(roots) outputs: -0.0004884619

# plotting the roots of f3
points(x = roots, y = rep(0, length(roots)), col = "red", pch = 16, cex = 1.5)

Output:

graphing the roots of f3

In conclusion, you should always plot the function and its solutions to make sure that the solution(s) outputted by uniroot.all() make sense.

Further reading