Índice

1. Ejercicio 1 (0,8 puntos).

1.1. enunciado

Explica cómo maximizar la función:

f <- function (x) sin(10/(.03-x)) + 1e4*(-x^2+0.03*x)

1.2. resolución

Es una parábola convexa hacia arriba más un seno, por lo que el máximo estará cerca de \(-b/2a = -0.03/(-2) = -0.015\), o sea, cerca del cero:

plot (f, -5, 5, type="l")
plot (f, -1, 1, type="l")
plot (f, -.1, .1, type="l")
Warning message:
In sin(10/(0.03 - x)) : Se han producido NaNs
plot (f, -.01, .01, type="l")
plot (f, 0, .02, type="l")
plot (f, 0, .02, type="l", n=1001)
plot (f, 0.014, .016, type="l", n=1001)
plot (f, 0.014, .016, type="l", n=1001, ylim=c(3,3.5))
plot (f, 0.014, .016, type="l", n=1001, ylim=c(3.2,3.5))
plot (f, 0.014, .016, type="l", n=1001, ylim=c(3.23,3.26))
plot (f, 0.014, .016, type="l", n=1001, ylim=c(3.24,3.255))
plot (f, 0.014, .016, type="l", n=10001, ylim=c(3.24,3.255))
plot (f, 0.0147, .0155, type="l", n=10001, ylim=c(3.24,3.255))
plot (f, 0.015, .0151, type="l", n=10001, ylim=c(3.24,3.255))
## cerca de 0.01502

Vamos a buscar cerca:

> optimize (f, c(0.015, 0.0151), maximum=TRUE)
$maximum
[1] 0.0150382

$objective
[1] 2.960766

> ## mal, demasiado lejos

> optimize (f, c(0.01501, 0.01503), maximum=TRUE)
$maximum
[1] 0.01501764

$objective
[1] 3.240644

> ## esa parece buena, pero
> abline(v=0.01501764,col=2) # no vale
> optim (0.01502, f, control=list(fnscale=-1)) $ par
[1] 0.01502071
Warning message:
In optim(0.01502, f, control = list(fnscale = -1)) :
  one-dimensional optimization by Nelder-Mead is unreliable:
use "Brent" or optimize() directly
> optim (0.01502, f, control=list(fnscale=-1), method="Brent") $ par
Error in optim(0.01502, f, control = list(fnscale = -1), method = "Brent") : 
  'lower' and 'upper' must be finite values
> optim (0.01502, f, control=list(fnscale=-1), method="Brent", lower=0.01501, upper=0.01503) $ par
[1] 0.01502071
> ## esta sí vale
> abline (v=0.01502071, col=3)

Otra posibilidad es usar recocido desde un principio, por ejemplo mediante optim,

> optim (0.015, f, method="SANN", control=list(fnscale=-1, maxit=1e3)) [1:2]
$par
[1] 0.0154359

$value
[1] 3.231594

> optim (0.015, f, method="SANN", control=list(fnscale=-1, maxit=1e4)) [1:2]
$par
[1] 0.014878

$value
[1] 3.249706

> optim (0.015, f, method="SANN", control=list(fnscale=-1, maxit=1e5)) [1:2]
$par
[1] 0.01502001

$value
[1] 3.249512

> optim (0.015, f, method="SANN", control=list(fnscale=-1, maxit=1e6)) [1:2]
$par
[1] 0.01502044

$value
[1] 3.249922

> optim (0.015, f, method="SANN", control=list(fnscale=-1, maxit=1e6)) [1:2]
$par
[1] 0.015021

$value
[1] 3.249912

> optim (0, f, method="SANN", control=list(fnscale=-1, maxit=1e6)) [1:2]
$par
[1] 0.01502062

$value
[1] 3.249988

2. Ejercicio 2 (0,8 puntos).

2.1. enunciado

Lo mismo, pero aprovechando un ordenador con dos hilos de concurrencia, o sea, de procesamiento en paralelo.

2.2. resolución

Por ejemplo, usando la última versión:

unavez <- function (x) # "x" no se usa, pero hace falta para mclapply
  optim (0.015, f, method="SANN", control=list(fnscale=-1, maxit=1e6)) [1:2]
library(parallel)
dosveces <- mclapply (1:2, unavez)
## como SANN es aleatorio, hay que combinar soluciones,
## por ejemplo, promediando:
mean (c (dosveces[[1]]$value, dosveces[[2]]$value))
## o buscando directamente la mejor de las dos: 
i <- which.max(c(dosveces[[1]]$value, dosveces[[2]]$value))
dosveces[[i]]$par 

3. Ejercicio 3 (0,8 puntos).

3.1. enunciado

El siguiente programa resuelve el problema de las reinas para un tablero n×n. Adáptalo para resolver el problema de las n torres (como el de las reinas, pero con torres; las reinas comen en vertical, horizontal y diagonal; las torres, sólo en vertical y horizontal).

3.2. resolución

Simplemente, eliminamos las restricciones asociadas a la captura diagonal:

## variables: Xij i=1..n j=1..n
objetivo <- rep (1, n*n)
restric.fil <- kronecker (diag(n), t(rep(1,n)))
restric.col <- kronecker (t(rep(1,n)), diag(n))
## restric.di1 <- matrix (0, n+n-1, n*n)
## for (i in 2:(2*n))
## restric.di1 [i-1, apply(expand.grid(1:n, 1:n), 1, sum) == i] <- 1
## restric.di2 <- matrix (0, n+n-1, n*n)
## for (i in (-n+1):(n-1))
## restric.di2 [i+n, apply(expand.grid(1:n, 1:n), 1, diff) == i] <- 1
restricciones <- rbind (restric.fil, restric.col # , restric.di1, restric.di2
                        )
lado.derecho <- rep (1, nrow(restricciones))
sol <- lpSolve::lp ("max", objetivo, restricciones, "<", lado.derecho, all.int =TRUE)
print (sol)
print.table (matrix(sol$solution,n), zero.print="-")

La solución óptima es trivial, colocando las torres en diagonal.

4. Ejercicio 4 (0,8 puntos).

4.1. enunciado

Explica cómo hallar la ruta más corta que recorra varias ciudades. Por ejemplo, resuélvelo para:

> ciu = c("Madrid","Barcelona","Gibraltar","Lisbon","Lyons","Paris")
> as.matrix(eurodist)[ciu,ciu] # distancias kilométricas
>           Madrid Barcelona Gibraltar Lisbon Lyons Paris
Madrid         0       636       698    668  1281  1273
Barcelona    636         0      1172   1305   645  1033
Gibraltar    698      1172         0    676  1817  1971
Lisbon       668      1305       676      0  1178  1799
Lyons       1281       645      1817   1178     0   471
Paris       1273      1033      1971   1799   471     0

4.2. resolución

ciu <- c("Madrid","Barcelona","Gibraltar","Lisbon","Lyons","Paris")
d <- as.matrix(eurodist)[ciu,ciu]
n <- nrow(d)
## una ruta sería una permutación de orden n
longitud <- function (ruta) sum (sapply (2:n, function (i) d[ruta[i-1],ruta[i]]))
## varias maneras:
## 1) recorriéndolas todas (720 para 6)
todas <- lapply (combinat::permn(6),
                 function (ruta) list(ruta=ruta, longitud=longitud(ruta)))
(mejor <- todas[[which.min(sapply(todas, function (x) x$longitud))]])
ciu[mejor$ruta]
## 2) muestreando
rutas <- replicate (10000, # >> 720
                    list(ruta = ruta <- sample(n),
                         longitud = longitud(ruta)))
(mejor <- rutas[,which.min(unlist(rutas[2,]))])
ciu[mejor$ruta]
## 3) recocido con optim
intercambiar <- function (ruta) {ruta[rev(ij)] <- ruta[ij <- sample(n,2)]; ruta}
(mejor <- optim(1:n, longitud, intercambiar, method="SANN"))
ciu[mejor$par]

5. Ejercicio 5 (0,8 puntos).

5.1. enunciado

Los siguientes renglones aparecen en la ayuda de optim:

fw <- function (x) 10*sin(0.3*x)*sin(1.3*x^2) + 0.00001*x^4 + 0.2*x+80
res <- optim(50, fw, method = "SANN",
             control = list(maxit = 20000, temp = 20, parscale = 20))

Determina la relevancia de los parámetros maxit, temp y parscale para la obtención de ese res.

5.2. resolución

maxit
es el número de iteraciones del recocido; cuanto mayor sea, mayor será la probabilidad de que la solución se encuentre cerca del óptimo.
temp
indica el valor inicial del parámetro temperatura; cuanto más alto, son necesarias más iteraciones para llegar al estado de equilibrio, es decir, que converja la solución.
parscale

al igual que fnscale es un coeficiente asociado a la función objetivo (de forma que, si es negativo, permite maximizar), parscale es un coeficiente asociado al par (parámetro), es decir, a la solución (vector de valores sobre el que se aplica el vector objetivo).

fw <- function (x) 10*sin(0.3*x)*sin(1.3*x^2) + 0.00001*x^4 + 0.2*x+80
res <- optim(50, fw, method = "SANN",
             control = list(maxit = 20000, temp = 20, parscale = 20))
res[c("par","value")]
plot (fw, -16, -15, ylim=c(67.45,67.5), n=1001) # el mínimo cerca de -15'8...
## ...pero a veces (casi 20%) el recocido cae en el mínimo local de -15'66:
table (round (replicate (10000,
                         optim(50, fw, method = "SANN",
                               control = list(maxit = 20000,
                                              temp = 20,
                                              parscale = 20))$par), 1))
##  -16 -15.8 -15.7 
##   11  8058  1931 

resf <- function (maxit=20000, temp=20, parscale=20)
  optim(50, fw, method = "SANN",
    control = list(maxit = maxit, temp = temp, parscale = parscale))$par
table (round (replicate (10000, resf(maxit=50000)), 1))
## -15.8 -15.7 
##  9826   174 => casi se asegura convergencia correcta con 50k iteraciones
table (round (replicate (1000, resf(maxit=5e4,temp=100)), 1)) # 80%
table (round (replicate (1000, resf(maxit=5e4,temp=50)), 1)) # 88%
table (round (replicate (1000, resf(maxit=5e4,temp=30)), 1)) # 94%
table (round (replicate (10000, resf(maxit=5e4,temp=10)), 1)) # 99% mejor que temp=20
table (round (replicate (10000, resf(maxit=5e4,temp=5)), 1)) # 98%
table (round (replicate (1000, resf(maxit=5e4,temp=1)), 1)) # 85%
## para 50k iteraciones, bien con 5<temp<20, mejor en torno a 10
table (round (replicate (1000, resf(maxit=5e4,temp=10,parscale=10)), 1)) # 77%
table (round (replicate (1000, resf(maxit=5e4,temp=10,parscale=30)), 1)) # 99%
table (round (replicate (1000, resf(maxit=5e4,temp=10,parscale=50)), 1)) # 95%
table (round (replicate (1000, resf(maxit=5e4,temp=10,parscale=40)), 1)) # 99%
## bien con 20<parscale<40 ; es un parámetro muy relevante en este problema
## sorprendentemente, porque la explicación dada en ?optim sugiere que sirve
## sólo para problemas multidimensionales ; debe de afectar a la distancia
## de la nueva solución a la anterior

Autor: Carlos Carleos

Created: 2023-01-20 vie 10:20

Validate