How to solve the following 3-dimensional system of non-linear inequalities in R?

136 Views Asked by At

I need to solve the following system of non-linear inequalities:

x*y >= 420
x*z >= 14
x*y*z < 5000

I have tried to find a similar question/solution or package that helps, but I struggle to apply it to that specific case.

The expected outcome should be a list of tuples (x,y,z). In the end, a 3-dimensional plot would be awesome but not really necessary (though should be easy to do it as soon as the solution list exists).

Edit: x, y and z are positive integers.

2

There are 2 best solutions below

0
On BEST ANSWER

NB: It was recently clarified that the solutions should consist of integers. I'll give a solution for integers first, then the original answer without that condition.

The last condition x*y*z < 5000 indicates that all of the values must be less than 5000. That makes an exhaustive search feasible.

The way I'd do it is to set y to all possible values (i.e. 1:4999), then for each value find all possible x values. That will give a long list of (x,y) pairs. Then for each z value, select the pairs that meet all the conditions. For example:

x0 <- 1:4999
y0 <- 1:4999
z0 <- 1:4999

xy <- matrix(numeric(), ncol = 2, nrow = 0)
for (y in y0) {
  keep <- x0*y >= 420 & x0*y < 5000
  if (any(keep))
    xy <- rbind(xy, cbind(x0[keep], y))
}

xyz <- matrix(numeric(), ncol = 3, nrow = 0)
for (z in z0) {
  keep <- xy[, 1]*z >= 14 & xy[,1]*xy[,2]*z < 5000
  if (any(keep))
    xyz <- rbind(xyz, cbind(xy[keep, , drop=FALSE], z))
}

str(xyz)
#>  num [1:61833, 1:3] 420 421 422 423 424 425 426 427 428 429 ...
#>  - attr(*, "dimnames")=List of 2
#>   ..$ : NULL
#>   ..$ : chr [1:3] "" "y" "z"
head(xyz)
#>          y z
#> [1,] 420 1 1
#> [2,] 421 1 1
#> [3,] 422 1 1
#> [4,] 423 1 1
#> [5,] 424 1 1
#> [6,] 425 1 1
tail(xyz)
#>              y  z
#> [61828,] 2 222 11
#> [61829,] 2 223 11
#> [61830,] 2 224 11
#> [61831,] 2 225 11
#> [61832,] 2 226 11
#> [61833,] 2 227 11

Created on 2022-12-09 with reprex v2.0.2

This is the original answer, without restricting to integer values:

The system of inequalities you give has an infinite number of solutions, so it's not possible to list them all in an R list. However, you could construct functions that describe the region holding the solutions.

One way to do that is to create two functions, with the first giving the range of y consistent with a particular x, and the second giving the range of z consistent with an (x, y) pair. For example,

min_y <- function(x) {
  420/x
}

range_z <- function(x, y) {
  lower <- 14/x
  upper <- 5000/x/y
  if (lower <= upper)
    c(lower, upper)
  else
    NULL
}

x <- 100
min_y(x)
#> [1] 4.2
range_z(x, min_y(x))
#> [1]  0.14000 11.90476
range_z(x, min_y(x) + 1)
#> [1] 0.140000 9.615385
range_z(x, min_y(x) + 1000)
#> NULL

Created on 2022-12-08 with reprex v2.0.2

This shows that if x is 100, y must be bigger than 4.2. If you set y to 4.2 with x = 100, then z can be anything from 0.14 to 11.90476. If you set y to 5.2, z has to be between 0.14 and 9.615385. And if you set y to 1004.2, there are no solutions for z at all.

0
On

Here is a way.
Reverse the >= inequalities and use the nleqslv solver.

library(nleqslv)

# x.y >= 420
# x.z >= 14
# x.y.z < 5000

fun <- function(x) {
  y <- numeric(3)
  y[1] <- 420 - x[1] * x[2]
  y[2] <- 14 - x[1] * x[3]
  y[3] <- prod(x) - 5000
  y
}

xstart <- c(1, 1, 1)
sol <- nleqslv(xstart, fun, method = "Newton")
cbind(x = sol$x, y = sol$fvec)
#>              x             y
#> [1,]   1.17600  8.154757e-10
#> [2,] 357.14286  2.486900e-14
#> [3,]  11.90476 -8.772076e-09

Created on 2022-12-08 with reprex v2.0.2

The second column of the results matrix above shows how close the solutions are from the conditions.

sol <- nleqslv(xstart, fun, method = "Newton")

x <- sol$x[1]
y <- sol$x[2]
z <- sol$x[3]

x*y - 420
#> [1] -8.154757e-10
x*z - 14
#> [1] -2.4869e-14
x*y*z - 5000
#> [1] -8.772076e-09

Created on 2022-12-08 with reprex v2.0.2