Chapter 20
LURN… To Solve Operations Research Problems

This chapter explains how to find solutions to several problems in Operations Research. More specifically, they are problems in Linear Programming (LP) and Integer Programming (IP) where the solution is found as the optimal value of an objective function subject to the existence of various constraints, all of which are expressed as linear functions. The difference between LP and IP is the possible values for the decision variables under consideration.

To benefit the most from this chapter, you ought to be familiar with basic manipulation of vectors and matrices, as seen in Chapter 19 and be ready to install an additional package using the methods outlined in Chapter 14. You will need to install the lpSolve package and make sure it is ready for use by calling the require() command as seen in the examples in this chapter. Note that this need only be done once, but the command is repeated as a reminder.

Note that all examples are intentionally made reproducible through use of random number generation. You will need to make sure you reset the random number generator’s seed value using the set.seed() commands that appear in the examples.

20.1 The Assignment Problem

The assignment problem assigns n tasks to n people. The objective function is often described in tabular or matrix form as a set of costs to be minimised. The constraints are that all tasks must be assigned and no person can complete more than one task. “Dummy" tasks or people are added to the matrix if the number of tasks is not equal to the number of people.

The mathematical programming representation for the assignment problem has the objective function:

    ∑n  ∑n
min         cijxij    i,j = 1,...,n
     i=1 j=1
(20.1)

subject to the constraints:

∑n
    xij  = 1,  ∀i = 1,...,n
j=1
 n
∑   x   =  1,  ∀j = 1, ...,n
     ij
 i=1
    xij    ∈ {0, 1}  ∀i,j

  > require(lpSolve)
  > set.seed(3010)
  > APCost.mat = matrix(sample(10:100, 16), nrow=4)
  > APCost.mat

       [,1] [,2] [,3] [,4]
  [1,]   24   45   25   31
  [2,]   21   46   86   55
  [3,]   87   35   99   12
  [4,]   60   88   10   97

  > Sol = lp.assign(APCost.mat)
  > names(Sol)

   [1] "direction"      "rcount"         "ccount"
   [4] "costs"          "rsigns"         "rrhs"
   [7] "csigns"         "crhs"           "objval"
  [10] "int.count"      "integers"       "solution"
  [13] "presolve"       "compute.sens"   "sens.coef.from"
  [16] "sens.coef.to"   "duals"          "duals.from"
  [19] "duals.to"       "status"

  > Sol$solution

       [,1] [,2] [,3] [,4]
  [1,]    0    1    0    0
  [2,]    1    0    0    0
  [3,]    0    0    0    1
  [4,]    0    0    1    0

  > Sol$objval

  [1] 88

  > Sol

  Success: the objective function is 88

Note that the print out of the solution didn’t actually tell us which task was to be completed by which person so we needed to look for the matrix of the final values for the decision variables. There are plenty of additional items stored as part of the solution, many of which you may never need. It is assumed that if your needs run to the advanced ideas using these items that you will now know where to find the necessary information to solve your own problems.

20.2 The transportation problem

The transportation problem aims to satisfy the demands of a set of customers by optimising the selection of the supplier who will meet each customer’s demands. The mathematical program for this problem has a cost matrix and upper bounds on supply and lower bounds on demands. The optimisation is a minimisation exercise so the demand is kept as low as is required so the lower bounds are (in essence) equality constraints as a consequence.

The objective function is:

    ∑m  ∑n
min        cijxij
    i=1 j=1
(20.2)

and the constraints are:

 n
∑
    xij ≤ si   ∀i = 1,...,m
j=1
∑m
   xij ≥ dj    ∀j =  1,...,n
i=1
    x   ≥ 0         ∀i,j
      ij

  > require(lpSolve)
  > set.seed(3010)
  > TPCost.mat = matrix(sample(10:100, 30), nrow = 5)
  > TPCost.mat

       [,1] [,2] [,3] [,4] [,5] [,6]
  [1,]   24   46   99   97   85   19
  [2,]   21   35   10   47   93   91
  [3,]   87   88   31   59   95   64
  [4,]   60   25   55   40   48   27
  [5,]   45   86   12   11   61   81

  > DemandSigns = rep (">", 6)
  > DemandLimits = sample(50:300, 6)
  > DemandLimits

  [1] 201 185 145 207  80  59

  > SupplySigns = rep ("<", 5)
  > SupplyLimits = sample(100:500, 5)
  > SupplyLimits

  [1] 457 376 351 132 388

  > Sol = lp.transport (TPCost.mat, "min", SupplySigns, SupplyLimits, DemandSigns, DemandLimits, compute.sens=0)
  > Sol$solution

       [,1] [,2] [,3] [,4] [,5] [,6]
  [1,]    0    0    0    0    0   59
  [2,]  201  133   42    0    0    0
  [3,]    0    0    0    0    0    0
  [4,]    0   52    0    0   80    0
  [5,]    0    0  103  207    0    0