# Programación Entera: Heurísticas para el Problema de Asignación

#### Descripción

En este trabajo se estudia el problema de asignación de manera detallada, en concreto el de asignación clásico y el de asignación generalizado, analizando el problema de programación lineal además de heurísticas greedy y de búsqueda local. Todo ello se ha hecho apoyandose en el lenguaje *R* y el solver *glpk*. Por último se ha comprobado la validez de los métodos sobre distintos problemas a modo de ejemplo.

Este documento forma parte de un grupo de trabajos relacionados con la heuristicas para la resolución de problemas de programación entera. Existen otros tres trabajos referidos al estudio del [problema de la mochila](https://nbviewer.jupyter.org/github/garciparedes/integer-programming-heuristics/blob/master/integer-programming-knapsack-heuristics.ipynb), a [problemas de redes](https://nbviewer.jupyter.org/github/garciparedes/integer-programming-heuristics/blob/master/integer-programming-network-heuristics.ipynb) y a [problemas de localización de servicios](https://nbviewer.jupyter.org/github/garciparedes/integer-programming-heuristics/blob/master/integer-programming-service-location-heuristics.ipynb). El contenido completo del trabajo está publicado en el siguiente repositorio: https://github.com/garciparedes/integer-programming-heuristics

#### Autor

  * Sergio García Prado - [garciparedes.me](https://garciparedes.me)

#### Fecha

  * Mayo de 2018

## Contenidos

  * [Introducción](#Introducción)
  * [Problema de Asignación](#Problema-de-Asignación)
    * [Asignación Clásico](#Asignación-Clásico)
    * [Asignación Generalizado](#Asignación-Generalizado)
  * [Conclusiones](#Conclusiones)

## Introducción

El estudio de problemas de programación lineal binaria es un área de investigación muy interesante y enriquecedor, por su amplio campo de apliación en situaciones reales. En este caso, el trabajo se centra en el estudio de problemas de asignación, es decir, aquellos que se basan en la búsqueda de la configuración óptima en algún sentido entre un conjunto de productores y otro de consumidores.

Estos problemas modelizan un gran número de situaciones. Entre ellas, una de las más destacadas es la asignación de tareas a un grupo de individuos de tal manera que se minimicen los costes globales de la planificación.

La resolución de óptima de problemas de programación lineal binaria conlleva un elevado coste computacional, que en muchos casos se hace inmanejable cuando el tamaño del conjunto de datos de entrada es muy elevado. Esto es cuando hay muchas tareas que asignar a muchos individuos. Por tanto, en la literatura se han propuesto distintas heurísticas, que a pesar de no ofrecer ninguna garantía de optimalidad, en la práctica ofrecen resultados muy cercanos a dicho valor con un coste computacional mucho menor.

En este trabajo se han estudiado dos casos concretos del problema de asignación: el referido a **asignación clásico**, donde tanto productores como consumidores sólo pueden relacionarse de manera **uno a uno**, y el referido a **asignación generalizada**, donde un productor puede servir a más de un consumidor, por lo que la relación es **uno a muchos**. Para cada tipo de problema, se ha estudiado el planteamiento exacto, basado en programación lineal, además de una heurística *Greedy* y una mejora basada en búsqueda local. Por último, se ha comprobado la validez de dichos planteamientos aplicando dichas técnicas a una serie de problemas reales.

Todo el planteamiento se ha llevado a cabo apoyandose en el lenguaje de computación estadística *R*, sobre el cual se han implementado las diferentes heurísticas, además de resolver los problemas de manera exacta. Para ello se ha utilizado el *solver* de de programación mixta (lineal y entera) **glpk**.

A continuación se muestran distintas sentencias para configurar el entorno de desarrollo, así como funciones de lectura de ficheros (`ReadAssignment()` para la lectura de los conjuntos de datos referidos a problemas de asignación clásica, `ReadGeneralizedAssignment()` para los correspondientes problemas de asignación generalizada). Por último, se ha definido un "wrapper" denominado `Solve()` para la llamada al solver *glpk*.

#### Configuración del Entorno

```R
rm(list = ls())
```

```R
library(slam)
library(Rglpk)
library(magrittr)
```

    Using the GLPK callable library version 4.65

```R
options(repr.matrix.max.rows = 600, repr.matrix.max.cols = 200)
```

#### Constantes y Funciones de Apoyo

```R
dataset.folder <- "./data/assignment/"
```

```R
ReadAssignment <- function(file.name) {
    c <- as.matrix(read.csv(file.name, header = FALSE))
    colnames(c) <- NULL
    list(
        m = dim(c)[1],
        n = dim(c)[2],
        cost = c
    ) %>%
    return()
}
```

```R
ReadGeneralizedAssignment <- function(file.name) {
    con <-
        file.name %>%
        file(open = "r")
    raw <-
        con %>%
        readLines(warn = FALSE) %>%
        strsplit("[[:blank:]]") %>%
        unlist() %>%
        as.integer()
    close(con)
    list(m        = raw[1],
         n        = raw[2],
         cost     = matrix(raw[3:(2 + raw[1] * raw[2])],
                           nrow = raw[1], ncol = raw[2], byrow = TRUE),
         resource = matrix(raw[(3 + raw[1] * raw[2]):(2 + 2 * raw[1] * raw[2])],
                           nrow = raw[1], ncol = raw[2], byrow = TRUE),
         capacity = raw[(3 + 2 * raw[1] * raw[2]):length(raw)]

    ) %>%
    return()
}
```

```R
Solve <- function(f.obj, f.con, f.dir, f.rhs, ...) {
    Rglpk_solve_LP(f.obj, f.con, f.dir, f.rhs, ...)
}
```

## Problema de Asignación

El *problema de asignación* es ampliamente conocido y ha sido estudiado en profundidad en la literatura, principalmente debido a que permite modelizar una gran cantidad de situaciones que se dan en la vida real. Por estas razones es necesario encontrar un modelo matemático que permita representar y estudiar el problema de manera analítica.

De manera natural, el problema de asignación puede ser enunciado como el modelo que representa aquellas situaciones en que se tiene dos grupos disjuntos de elementos, tales que el primero de ellos está constituido por productores mientras que el segundo grupo se compone de consumidores. El problema se basa en asignar a cada consumidor un determinado productor, de tal manera que todos ellos queden cubiertos. Este problema puede ser ampliado de muchas maneras, tal y como se verá posteriormente, sin embargo el objetivo último es minimizar (o maximizar) una determinada función de coste (o beneficio) encontrando la configuración óptima de asignaciones.

Tal y como se puede apreciar en el párrafo anterior, este problema puede modelizarse como de programación lineal binaria, de tal manera que cada variable represente de si un productor sirve on no a un consumidor restringido a unas determinadas restricciones de capacidad y demanda (dependiendo del modelo concreto). No es difícil darse que este problema es muy similar al [Problema de Transporte](https://es.wikipedia.org/wiki/Problema_de_transporte), tal es así que la diferencia reside en la imposición de que las variables de decisión sean de naturaleza binaria. Por tanto, el problema de transporte puede ser visto como una relajación del problema de asignación.

A continuación, se estudirán de manera detallada dos problemas enmarcados en la categoría de problemas de asignación, siendo el primero de ellos el de [Asignación Clásico](#Asignación-Clásico), y el segundo [Asignación Generalizado](#Asignación-Generalizado). Para estos problemas se va a definir el modelo de programación lineal que los representa, para posteriormente añadir el código fuente en el lenguaje *R* que permite la resolución *Exacta* de dichos problemas. Además, se indicarán las heurísticas *Greedy* junto con su correspondiente mejora de *Búsqueda Local*, todas ellas implementadas en el lenguaje *R* para comprobar su efectividad. Por último se han resuelto distintos problemas (referidos a cada modelización) para comprobar la validez de las heurísticas, así como su cercanía a la solución óptima obtenida por el método exacto.

### Asignación Clásico

#### Descripción:

El *problema de asignación clásico* se corresponde con el caso más básico posible, en el cual el número de productores y de consumidores es el mismo, y el objetivo es la búsqueda de la asignación óptima siguiendo la configuración **1 productor a 1 consumidor** de tal manera que se minimice (o maximice) una determinada función de coste.

Para ello, la modelización más sencilla es la basada en una matriz binaria de variables de decisión denominada $X$ y formada por $m$ filas que representan a los productores y $n$ columnas que representan a los consumidores. De esta manera se dice que si la posición $x_{ij}$ de la matriz contiene el valor $1$ el productor $i$ servirá al consumidor $j$, mientras que no lo hará si esta posición contiene el valor $0$. Para poder cuantificar el coste (o valor) de cada una de las posiciones de la matriz de decisiones y así encontrar la solución óptima para el problema, nos apoyaremos en una matriz de costes denotada por $C$ y con las mismas dimensiones. Las restricciones sobre las que se define este problema por tanto, pueden ser enunciadas de tal manera que cada productor sirva únicamente a un consumidor, así como que cada consumidor sea servido únicamente por un productor.

#### Modelo:

Una vez descrita la notación necesaria para poder modelizar el problema así como una descripción acerca de las restricciones necesarias para que este sea coherente con la realidad que pretender representar, el siguiente paso es indicar su formulación:

\begin{equation}
    \begin{array}{ll@{}ll}
      \text{Minimizar} & \displaystyle \sum\limits_{i=1}^{m}\sum\limits_{j=1}^{n} c_{ij} \cdot x_{ij} \\
      \text{sujeto a}  & \sum\limits_{j=1}^{n} x_{ij} = 1, & \forall i \in \{1,...,m\} \\
                       & \sum\limits_{i=1}^{m} x_{ij} = 1, & \forall j \in \{1,...,n\} \\
                       & x_{ij} \in \{0, 1\}, 	& \forall i \in \{1,...,m\}, \forall j \in \{1,...,n\}
    \end{array}
\end{equation}

Una vez definido el *problema de asignación clásico* de tal manera que se pueda construir de manera clara y bien definida una función objetivo, así como una matriz de restricciones, el siguiente paso es realizar la codificación pertinente en el lenguaje *R*, la cual se puede consultar en la función `AssignmentExact()`, que tan solo requiere de una matriz de costes para e indicar si el problema el sentido del problema (minimización o maximización) para devolver la solución óptima del problema.

```R
AssignmentExact <- function(p, ...) {
    conditions <- matrix(0, nrow = p$n + p$m, ncol = p$n * p$m)
    for (i in 1:p$m) {
        conditions[i, seq(p$n) + (i - 1) * p$n] <- 1
    }
    for (i in (p$m + 1):(p$n + p$m)) {
        conditions[i, seq(i - p$m, p$m * p$n, by = p$n)] <- 1
    }
    Solve(p$cost, conditions, c(rep("==", p$m + p$n)),
          rep(1, p$n + p$m),  ...)
}
```

#### Heurísticas:

La dificultad práctica que surge mediante la resolución exacta del problema se debe principalemente al coste computacional necesario para llegar a la solución deseada cuando el tamaño de la matriz de costes es elevado. Por tanto, existen otras técnicas conocidas como heurísticas que a pesar de no suministrar un resultado óptimo en todos los casos, generan resultados próximos a él con un coste computacinal mucho menor. Es por ello que a continuación se van a enunciar dos heurísticas que permiten resolver el problema en un tiempo mucho menor. Primeramente describiremos el funcionamiento de la heuristica *Greedy* para posteriormente realizar una mejora sobre ella con el método de *Busqueda Local*.

##### Greedy:

La heurística *Greedy* recibe ese nombre precisamente debido por su método de comportamiento. La traducción a lenguaje Castellano podría ser algo así como *hambriento* o *voraz* ya que esta nunca "retrocede" y cambia una decisión tomada. Es decir, una vez fijada una asignación entre unos determinados productores y consumidores, esta no cambia para buscar otra configuración más cercana al óptimo real. Esto hace que el la implementación algorítmica de la solución Greedy sea extremadamente eficiente, ofreciendo resultados razonables. La regla de decisión que utiliza es la elección de la asignación que más minimiza (o maximiza) la función de coste de entre todas las posibles asignaciones coherentes en esa iteracción. Esto quiere decir que si un productor ya ha sido utilizado, no tendrá sentido estudiar si se puede asignar a otro consumidor, y viceversa.

A continaución se ilustra la implementación realizada de la heurística *Greedy*, recogida en la función `AssignmentGreedy()`, que tan solo requiere de la matriz de costes así como de la métrica a optimizar, para devolver una solución aproximada en un tiempo reducido:

```R
AssignmentGreedy <- function(p) {
    demand <- rep(1, dim(p$cost)[1])
    capacity <- rep(1, dim(p$cost)[2])
    sol <- list(solution = matrix(0, nrow = p$m, ncol = p$n),
                objval = 0, slack = capacity)
    while (!all(demand == 0)) {
        ij <- which(p$cost == min(p$cost[sol$slack > 0, demand > 0]), arr.ind = TRUE)
        ij.slice <- which(sol$slack[ij[, 1]] > 0 & demand[ij[, 2]] > 0)[1]
        i <- ij[ij.slice, 1]
        j <- ij[ij.slice, 2]
        inc <- min(sol$slack[i], demand[j])

        sol$solution[i, j] <- sol$solution[i, j] + inc
        sol$slack[i] <- sol$slack[i] - inc
        demand[j] <- demand[j] - inc
        sol$objval <- sol$objval + p$cost[i, j, drop = TRUE] * inc
    }
    return(sol)
}
```

##### Búsqueda Local:

La heurística de *Búsqueda Local* en realidad es una técnica para optimizar soluciones iniciales y tratar de mejorar en la medida de lo posible (un entorno local) la solución inicial. Esto se lleva a cabo de la siguiente manera: Partiendo de una solución inicial que asumimos factible, el método trata de realizar una permutación entre los consumidores de dos productores de tal manera que el coste se reduzca. Al igual que sucedía en el caso de la heurística *Greedy*, ahora también se sigue la misma filosofía, en el sentido de que en cada iteracción del método, se realiza el cambio que mayor beneficio proporciona.

La implementación de esta solución se ha dividido en tres partes, partiendo inicialmente de una solución obtenida por el método *Greedy*. El siguiente paso a realizar es definir la heurística de mejora o beneficio, la cual se ha llevado a cabo en la función `GreedyLocalSearchImprovement()`, que dados dos pares de índices (referidos dos asignaciónes (productor, consumidor)). Este beneficio se calcula de la siguiente manera:

$$(c_{i_1j_2} + c_{i_2j_1}) - (c_{i_1j_1} + c_{i_2j_2})$$

Es decir, el beneficio será el nuevo coste de la asignación (dos primeros términos) menos el coste anterior de la asignación (dos últimos términos). Una vez calculados dichos valores, es necesario quedarse con el máximo de entre todos ellos, lo cual se lleva a cabo a partir de la función `GreedyLocalSearchBest()`. El último paso es el de realizar el cambio. Dicho cambio se realiza en la función que controla el algoritmo de búsqueda local (finaliza cuando ya no hay nuevos cambios que mejoren el la función objetivo). Esta función se ha denotado por `AssignmentGreedyLocalSearch()`.

```R
GreedyLocalSearchImprovement <- function(change.1, change.2, cost) {
    (cost[change.1[, drop = FALSE]] + cost[change.2[, drop = FALSE]]) -
    (cost[change.1[1], change.2[2]] + cost[change.2[1], change.1[2]])
}
```

```R
GreedyLocalSearchBest <- function(cost, changes) {
    best = list(v = 0, i = 0, j = 0)
    for (i in 1:(nrow(changes) - 1)) {
        for(j in (i + 1):nrow(changes)) {
            v.temp <- GreedyLocalSearchImprovement(changes[i, ,drop = FALSE],
                                                   changes[j, , drop = FALSE],
                                                   cost)
            if (best$v < v.temp) {
                best$v <- v.temp
                best$i <- i
                best$j <- j
            }
        }
    }
    return(best)
}
```

```R
AssignmentGreedyLocalSearch <- function(p) {
    sol <- AssignmentGreedy(p)

    changes <- which(sol$solution != 0, arr.ind = TRUE)
    best <- GreedyLocalSearchBest(p$cost, changes)
    while(best$v > 0) {

        sol$solution[changes[best$i, , drop = FALSE]] <- 0
        sol$solution[changes[best$j, , drop = FALSE]] <- 0

        temp <- changes[best$i, 2]
        changes[best$i, 2] <- changes[best$j, 2]
        changes[best$j, 2] <- temp

        sol$solution[changes[best$i, , drop = FALSE]] <- 1
        sol$solution[changes[best$j, , drop = FALSE]] <- 1

        sol$objval <- sol$objval - best$v

        best <- GreedyLocalSearchBest(p$cost, changes)
    }
    return(sol)
}
```

### Ejemplos

A continuación se va a demostrar la validez de los algoritmos implementados mediante la prueba sobre tres problemas de minimización de tamaño $15x15$ los cuales se definen a partir de la matriz de costes. Nótese que en estos casos no es necesario indicar en el procedimiento de resolución exacta que la solución dada debe ser binaria y no real, debido a la *propiedad de integridad* que asegura que si los coeficientes de un problema de transporte son enteras, entonces los valores de las variables de decisión también lo serán. Dado que el problema de asignación clásico es una caracterización del problema de transporte, esta condición también se cumplirá.

##### matriz.15x15.1

```R
matriz.15x15.1 <- ReadAssignment(paste0(dataset.folder, "matriz_15x15_1.csv"))
```

```R
s <- AssignmentExact(matriz.15x15.1)
list(solution = matrix(s$solution, nrow = matriz.15x15.1$n),
     objval   = s$optimum)
```

$solution

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

$objval

135

```R
s <- AssignmentGreedy(matriz.15x15.1)
list(solution = s$solution,
     objval   = s$objval)
```

$solution

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

$objval

218

```R
s <- AssignmentGreedyLocalSearch(matriz.15x15.1)
list(solution = s$solution,
     objval   = s$objval)
```

$solution

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

$objval

144

Tal y como se puede apreciar a la vista de los resultados, para el problema [matriz.15x15.1](#matriz.15x15.1), la solución óptima ($135$) siempre es menor que la obtenida por los métodos *Greedy* ($218$) y *Búsqueda Local* ($144$). Sin embargo, tal y como era de esperar, la solución optimizada a partir de la búsqueda local mejora de manera sustancial el resultado *Greedy*. A pesar de no llegar a tomar el valor óptimo, está muy cercano de manera relativa.

##### matriz.15x15.2

```R
matriz.15x15.2 <- ReadAssignment(paste0(dataset.folder, "matriz_15x15_2.csv"))
```

```R
s <- AssignmentExact(matriz.15x15.2)
list(solution = matrix(s$solution, nrow = matriz.15x15.2$m),
     objval   = s$optimum)
```

$solution

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

$objval

125

```R
s <- AssignmentGreedy(matriz.15x15.2)
list(solution = s$solution,
     objval   = s$objval)
```

$solution

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

$objval

146

```R
s <- AssignmentGreedyLocalSearch(matriz.15x15.2)
list(solution = s$solution,
     objval   = s$objval)
```

$solution

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

$objval

146

Tal y como se puede apreciar a la vista de los resultados, para el problema [matriz.15x15.2](#matriz.15x15.2), la solución óptima ($125$) siempre es menor que la obtenida por los métodos *Greedy* ($146$) y *Búsqueda Local* ($146$). En este caso, la optimización por búsqueda local no consigue mejorar la solución *Greedy* obtenida inicialmente. Sin embargo, esta solución se encuentra cerca del óptimo. Una situación que se da en muchos casos es que cuando las soluciones iniciales (en este caso la obtenida por el método *Greedy*) se encuentran cerca del valor óptimo, son difíciles de optimizar por métodos de búsqueda local, ya que el espacio de posibles alternativas de mejora es reducido.

##### matriz.15x15.3

```R
matriz.15x15.3 <- ReadAssignment(paste0(dataset.folder, "matriz_15x15_3.csv"))
```

```R
s <- AssignmentExact(matriz.15x15.3)
list(solution = matrix(s$solution, nrow = matriz.15x15.3$m),
     objval   = s$optimum)
```

$solution

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	0

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

$objval

153

```R
s <- AssignmentGreedy(matriz.15x15.3)
list(solution = s$solution,
     objval   = s$objval)
```

$solution

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0

$objval

205

```R
s <- AssignmentGreedyLocalSearch(matriz.15x15.3)
list(solution = matrix(s$solution, nrow = matriz.15x15.3$m),
     objval  = s$objval)
```

$solution

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	0

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

$objval

156

Tal y como se puede apreciar a la vista de los resultados, para el problema [matriz.15x15.3](#matriz.15x15.3), la solución óptima ($153$) siempre es menor que la obtenida por los métodos *Greedy* ($205$) y *Búsqueda Local* ($156$). Sin embargo, tal y como era de esperar, la solución optimizada a partir de la búsqueda local mejora de manera sustancial el resultado *Greedy*. A pesar de no llegar a tomar el valor óptimo, está muy cercano de manera relativa.

### Asignación Generalizado

#### Descripción:

El *problema de asignación generalizado* en cierto sentido, es una ampliación respecto del descrito en la sección anterior. Esto es la modificación de una la restricción impuesta a los productores, de tal manera que estos ya no estén restringidos a la servir a un único consumidor. Esto se puede modelizar en el enfoque relacional de bases de datos como una la relación **1 productor a muchos consumidores**. Sin embargo, la restricción no se elimina por completo, sino que se fija en una determinada "capacidad" de trabajo por cada productor. Tal y como es de esperar (y para que tenga sentido la anterior afirmación), también será necesario añadir una matriz extra que refleje las unidades de capacidad necesarias por cada productor para poder poder servir a cada consumidor.

Tal y como sucedía con el problema anterior, en este caso también se cumple la idea de que el problema de asignación generalizado se puede ver como el caso del problema del transporte, para el cual las variables de decisión son de naturaleza binaria. Nótese que en este caso el modelo se encuntra "más cercano aún" ya que las capacidades no son todas ellas iguales a $1$, cosa que si sucede con las demandas.

Ahora que ya se ha ilustrado el contexto del problema, el siguiente paso es definir las variables necesarias para poder modelizarlo y que este puedas ser estudiado de manera analítica. Al igual que para el problema de asignación clásico, se tiene una matriz de decisión binaria $X$ de $m$ filas y $n$ columnas referida a las asignaciones entre el productor $i$ y el consumidor $j$. La matriz de costes $C$ a minimizar (o maximizar) también es equivalente. Las diferencias residen una nueva matriz de capacidades con las mismas dimensiones que las anteriores y que denotaremos por $R$ (del inglés *resources*) y que indica en la posición $r_{ij}$ la cantidad de recursos necesarios para que el productor $i$ pueda satisfacer al consumidor $j$. Estos deberán ser menores o iguales a la capacidad de cada productor, que almacenaremos en un vector de longitud igual al número de productores y que denotaremor por $b$.

#### Modelo:

Una vez descrita la notación pertinente, así como las modificaciones sobre el problema de asignación clásico para llegar al problema de asignación generalizado, el siguiente paso es añadir el modelo matemático de programación lineal que representa el problema. La formulación del mismo se indica a continuación:

\begin{equation}
    \begin{array}{ll@{}ll}
      \text{Minimizar} & \displaystyle \sum\limits_{i=1}^{m}\sum\limits_{j=1}^{n} c_{ij} \cdot x_{ij} \\
      \text{sujeto a}  & \sum\limits_{i=1}^{m} x_{ij} = 1, & \forall j \in \{1,...,n\} \\
                       & \sum\limits_{j=1}^{n} r_{ij} x_{ij} \leq b_{i}, & \forall i \in \{1,...,m\} \\
                       & x_{ij} \in \{0, 1\}, 	& \forall i \in \{1,...,m\}, \forall j \in \{1,...,n\}
    \end{array}
\end{equation}

Una vez definido el modelo, es sencillo realizar la implementación pertinente en el lenguaje *R*. Esto se ha llevado a cabo en la función `GeneralizedAssignmentExact()` que calcula una solución exacta y para el problema de asignación generalizado:

```R
GeneralizedAssignmentExact <- function(p, ...) {
    conditions <- matrix(0, nrow = p$n + p$m, ncol = p$n * p$m)
    for (i in 1:p$m) {
        conditions[i, seq(p$n) + (i - 1) * p$n] <- p$resource[i, ]
    }
    for (i in (p$m + 1):(p$n + p$m)) {
        conditions[i, seq(i - p$m, p$m * p$n, by = p$n)] <- 1
    }
    Solve(t(p$cost), conditions, c(rep("<=", p$m), rep("==", p$n)),
          c(p$capacity, rep(1, p$n)), types = "B", max = TRUE, ...)
}
```

#### Heurísticas:

Debido al elevado coste computacional necesario para la resolución de manera exacta de problemas de asignación generalizada cuando el tamaño del problema es grande, a continuación se van a enunciar dos heurísticas que permiten resolver el problema de manera aproximada en un tiempo razonable. La primera de ellas está basada en el método *Greedy* del cual se ha hablado en la anterior sección, mientras que el segundo se refiere a otra heurística de mejora basada en *Búsqueda Local*, la cual también se describió en la anterior sección.

##### Greedy de Martello-Toth:

Este método, al igual que el descrito en la sección anterior, se basa en la asinación de productores y consumidores de manera *voraz* y sin tener en cuenta decisiones pasadas. Sin embargo, en el caso anterior no era necesario tener en cuenta la capacidad restante de los consumidores para llegar a una solución factible, algo que en este caso si que lo es. Por tanto, la heurística *Greedy* que se ha utilizado se basa en la selección de la configuración que la capacidad libre total de los productores, y de entre estas, se selecciona la que más ganancia proporcione en el sentido del problema (minimizar o maximizar). Mediante la utilización de esta heurística, se asegura la obtención de una solución factible del problema. Sin embargo, las garantías de optimalidad en este caso son más difusas que en el caso del problema anterior.

La función `GeneralizedAssignmentGreedy()` implementa el cálculo de la heurística *Greedy Martello-Toth* tal y como se ha descrito en el párrafo anterior.

```R
GeneralizedAssignmentGreedy <- function(p) {
    sol <- list(solution = matrix(0, nrow = p$m, ncol = p$n),
                objval = 0, slack = p$capacity)
    while(!all(colSums(sol$solution) == 1)) {
        max.resource <- max(sol$slack - p$resource[,colSums(sol$solution) == 0])

        ij <- which(sol$slack - p$resource == max.resource, arr.ind = TRUE)
        ij <- ij[colSums(sol$solution[, ij[,2], drop = FALSE]) == 0, , drop = FALSE]
        ij <- ij[which.max(p$cost[ij]), ,drop = FALSE]

        sol$solution[ij[1], ij[2]] <- 1
        sol$slack[ij[1]] <- sol$slack[ij[1]] - p$resource[ij[1], ij[2]]
        sol$objval <- sol$objval + p$cost[ij[1], ij[2]]
    }
    return(sol)
}
```

##### Búsqueda Local:

La heurística de *búsqueda local* para el problema de asignación generalizado, se basa en las mismas ideas que para el caso del problema de asignación clásico. Sin embargo, al igual que sucede con la heurística greedy, en este caso el intercambio de asignaciones debe tener en cuenta la capacidad de los productores. Esto se lleva a cabo mediante la verificación de que el nuevo intercambio cumplirá las restricciones de capacidad del productor.

La implementación de esta solución se ha dividido en tres partes, partiendo inicialmente de una solución obtenida por el método *Greedy*. El siguiente paso a realizar es definir la heurística de mejora o beneficio, la cual se ha llevado a cabo en la función `GeneralizedAssignmentGreedyLocalSearchmprovement()`, que dados dos pares de índices (referidos dos asignaciónes (productor, consumidor)). Dado que en este caso el problema de asignación calculará la mejora de beneficio de forma opuesta. Este beneficio se calcula de la siguiente manera:

$$- (c_{i_1j_2} + c_{i_2j_1}) + (c_{i_1j_1} + c_{i_2j_2})$$

Es decir, el beneficio será el nuevo coste de la asignación (dos primeros términos) menos el coste anterior de la asignación (dos últimos términos). Una vez calculados dichos valores, es necesario quedarse con el máximo de entre todos ellos, lo cual se lleva a cabo a partir de la función `GeneralizedAssignmentGreedyLocalSearchBest()`. El último paso es el de realizar el cambio. Dicho cambio se realiza en la función que controla el algoritmo de búsqueda local (finaliza cuando ya no hay nuevos cambios que mejoren el la función objetivo). Esta función se ha denotado por `GeneralizedAssignmentGreedyLocalSearch()`.

```R
GeneralizedAssignmentGreedyLocalSearchmprovement <- function(change.1, change.2, cost) {
    return( - (cost[change.1[, drop = FALSE]] + cost[change.2[, drop = FALSE]]) +
              (cost[change.1[1], change.2[2]] + cost[change.2[1], change.1[2]]))
}
```

```R
GeneralizedAssignmentGreedyLocalSearchBest <- function(p, changes, slack) {
    best = list(v = 0, i = 0, j = 0)
    for (i in 1:(nrow(changes) - 1)) {
        for(j in (i + 1):nrow(changes)) {
            if ((slack[changes[i, , drop = FALSE][1]] + p$resource[changes[i, , drop = FALSE]] -
                     p$resource[changes[i, 1], changes[j, 2]] > 0) &
                (slack[changes[j, , drop = FALSE][1]] + p$resource[changes[j, , drop = FALSE]] -
                     p$resource[changes[j, 1], changes[i, 2]] > 0)) {

                    v.temp <- GeneralizedAssignmentGreedyLocalSearchmprovement(changes[i, ,drop = FALSE],
                                                                     changes[j, , drop = FALSE],
                                                                     p$cost)
                    if (best$v < v.temp) {
                        best$v <- v.temp
                        best$i <- i
                        best$j <- j
                    }
                }
        }
    }
    return(best)
}
```

```R
GeneralizedAssignmentGreedyLocalSearch <- function(p) {
    sol <- GeneralizedAssignmentGreedy(p)

    changes <- which(sol$solution != 0, arr.ind = TRUE)
    slack <- rowSums(sol$solution * p$resource)
    best <- GeneralizedAssignmentGreedyLocalSearchBest(p, changes, sol$slack)
    while(best$v > 0) {

        sol$solution[changes[best$i, , drop = FALSE]] <- 0
        sol$solution[changes[best$j, , drop = FALSE]] <- 0
        sol$slack[changes[best$i][1]] <- sol$slack[changes[best$i][1]] + p$resource[changes[best$i, , drop = FALSE]]
        sol$slack[changes[best$j][1]] <- sol$slack[changes[best$j][1]] + p$resource[changes[best$j, , drop = FALSE]]

        temp <- changes[best$i, 2]
        changes[best$i, 2] <- changes[best$j, 2]
        changes[best$j, 2] <- temp

        sol$solution[changes[best$i, , drop = FALSE]] <- 1
        sol$solution[changes[best$j, , drop = FALSE]] <- 1

        sol$slack[changes[best$i][1]] <- sol$slack[changes[best$i][1]] - p$resource[changes[best$i, , drop = FALSE]]
        sol$slack[changes[best$j][1]] <- sol$slack[changes[best$j][1]] - p$resource[changes[best$j, , drop = FALSE]]
        sol$objval <- sol$objval + best$v
        best <- GeneralizedAssignmentGreedyLocalSearchBest(p, changes, sol$slack)
    }
    return(sol)
}
```

### Ejemplos

Para verificar la validez de las implementaciones realizadas, en esta sección se han aplicado sobre $5$ problemas de asignación generalizada para así poder comparar los resultados obtenidos.

##### gap2.1

```R
gap2.1 <- ReadGeneralizedAssignment("./data/assignment/gap2_1.txt")
```

```R
s <- GeneralizedAssignmentExact(gap2.1)
list(solution = matrix(s$solution, nrow = gap2.1$m, byrow = TRUE),
     objval   = s$optimum,
     slack    = s$auxiliary$primal[1:gap2.1$m])
```

$solution

0	0	1	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	1	0	0	1	0

1	0	0	0	1	0	0	0	0	0	0	0	1	0	0	0	1	0	0	0

0	0	0	1	0	0	0	1	0	0	0	0	0	0	1	0	0	1	0	0

0	1	0	0	0	0	0	0	0	0	1	1	0	1	0	0	0	0	0	0

$objval

434

$slack

38

50

51

43

54

```R
GeneralizedAssignmentGreedy(gap2.1)
```

$solution

0	0	1	0	0	0	0	0	1	0	0	1	0	0	0	0	0	1	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	1	0	0	1	0

0	0	0	0	0	1	0	1	0	0	0	0	1	0	0	0	0	0	0	1

1	0	0	1	0	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0

0	1	0	0	1	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0

$objval

377

$slack

14

14

13

13

10

```R
GeneralizedAssignmentGreedyLocalSearch(gap2.1)
```

$solution

0	0	0	0	0	0	0	0	1	0	1	1	0	0	0	0	0	0	0	1

0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	1	0	0	1	0

1	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	1	0	0	0

0	0	0	1	0	0	0	1	0	0	0	0	0	0	1	0	0	1	0	0

0	1	1	0	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

$objval

432

$slack

2

4

2

4

7

Para el problema [gap2.1](#gap2.1) las soluciones obtenidas han sido las siguientes: el resultado exacto del problema es ($434$), el resultado obtenido por el método *Greedy* propuesto por Martello-Toth es ($377$), que tras la heurística de *Búsqueda Local* toma el valor ($432$).

##### gap4.1

```R
gap4.1 <- ReadGeneralizedAssignment("./data/assignment/gap4_1.txt")
```

```R
s <- GeneralizedAssignmentExact(gap4.1)
list(solution = matrix(s$solution, nrow = gap4.1$m, byrow = TRUE),
     objval   = s$optimum,
     slack    = s$auxiliary$primal[1:gap4.1$m])
```

$solution

0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	1	0	1	0	0

0	1	0	0	1	0	0	0	0	0	1	0	1	0	0	1	1	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	1	1	0	0	0	0	1	0	0	0	0	1	0	0	0	0	1	0	0	1	0	0	0

1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	0	1	1	0	1	0	0	0	0	0

0	0	1	1	0	1	0	0	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	1

$objval

656

$slack

70

71

79

61

63

```R
s <- GeneralizedAssignmentGreedy(gap4.1)
list(solution = s$solution,
     objval   = s$objval,
     slack    = s$slack)
```

$solution

1	0	0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1	1

0	0	0	0	1	0	1	0	0	0	1	0	1	0	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	0

0	0	0	0	0	1	0	0	0	0	0	0	0	1	0	0	0	0	1	0	1	0	0	0	0	1	1	0	0	0

0	0	0	1	0	0	0	0	0	1	0	0	0	0	0	1	1	1	0	0	0	0	0	1	0	0	0	0	0	0

0	1	1	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0	0	1	0	0	0	0	0

$objval

541

$slack

19

28

11

20

20

```R
s <- GeneralizedAssignmentGreedyLocalSearch(gap4.1)
list(solution = s$solution,
     objval   = s$objval,
     slack    = s$slack)
```

$solution

0	0	0	0	0	0	1	0	1	0	1	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	1	0

0	1	0	0	1	0	0	0	0	0	0	1	1	0	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	0	1	0	0	0	0	1	0	0	0	0	1	0	1	1	0	0	0

1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	0	0	0	1	1	0	1	0	0	0	0	0

0	0	1	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	0	0	1

$objval

650

$slack

10

1

5

3

4

Para el problema [gap4.1](#gap4.1) las soluciones obtenidas han sido las siguientes: el resultado exacto del problema es ($656$), el resultado obtenido por el método *Greedy* propuesto por Martello-Toth es ($541$), que tras la heurística de *Búsqueda Local* toma el valor ($650$).

##### gap6.1

```R
gap6.1 <- ReadGeneralizedAssignment("./data/assignment/gap6_1.txt")
```

```R
s <- GeneralizedAssignmentExact(gap6.1)
list(solution = matrix(s$solution, nrow = gap6.1$m, byrow = TRUE),
     objval   = s$optimum,
     slack    = s$auxiliary$primal[1:gap6.1$m])
```

$solution

0	0	0	0	0	1	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	1	1	0	0	1	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0

1	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	1	1	0	0

0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	1	1	0	0	0	0	0

0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	1	0

0	0	0	0	0	0	0	0	1	1	1	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

$objval

761

$slack

37

39

44

49

48

44

42

43

```R
s <- GeneralizedAssignmentGreedy(gap6.1)
list(solution = s$solution,
     objval   = s$objval,
     slack    = s$slack)
```

$solution

1	0	0	0	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1	0	1	1	0	0	0	0	0	0	0	0	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	0	0	0	1	1	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	0	0	0	1	0	0	0	0	1	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	1	0	1	1	0	1	0	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0

0	0	1	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

$objval

664

$slack

26

9

14

25

24

14

21

20

```R
s <- GeneralizedAssignmentGreedyLocalSearch(gap6.1)
list(solution = s$solution,
     objval   = s$objval,
     slack    = s$slack)
```

$solution

0	0	0	0	0	1	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	1	1	0	0	1	0	0	0	0	0	0	0	0	0

0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	1	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	0	0	1	0	0	0	1	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	1	0	1	0	0	0	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	1	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

$objval

746

$slack

5

3

1

4

7

6

6

2

Para el problema [gap6.1](#gap6.1) las soluciones obtenidas han sido las siguientes: el resultado exacto del problema es ($761$), el resultado obtenido por el método *Greedy* propuesto por Martello-Toth es ($664$), que tras la heurística de *Búsqueda Local* toma el valor ($746$).

##### gap8.1

```R
gap8.1 <- ReadGeneralizedAssignment("./data/assignment/gap8_1.txt")
```

```R
s <- GeneralizedAssignmentExact(gap8.1)
list(solution = matrix(s$solution, nrow = gap8.1$m, byrow = TRUE),
     objval   = s$optimum,
     slack    = s$auxiliary$primal[1:gap8.1$m])
```

$solution

1	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0

0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	1	0	0	0	1	1	0	0

0	0	0	1	0	0	0	0	1	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0	1	0	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	1	0

0	0	0	0	1	1	0	1	0	0	0	0	0	0	0	1	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	1	0	0	0	0	0

$objval

1133

$slack

48

53

52

52

45

50

53

46

```R
s <- GeneralizedAssignmentGreedy(gap8.1)
list(solution = s$solution,
     objval   = s$objval,
     slack    = s$slack)
```

$solution

0	1	0	1	0	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0	0

1	0	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	1	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	0	1	0	1	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	1	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	1	0	0	1	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	1

0	0	0	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	1	0	0	0	1	0	0	1	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	1	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0

$objval

990

$slack

8

16

14

17

6

12

8

16

```R
s <- GeneralizedAssignmentGreedyLocalSearch(gap8.1)
list(solution = s$solution,
     objval   = s$objval,
     slack    = s$slack)
```

$solution

0	1	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0

0	0	1	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	1	0	1	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	1	0	1	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1	0	0	0	0	1	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	1

1	0	0	0	1	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	1	1	1	0	0	0	1	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	1	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

$objval

1100

$slack

1

2

1

2

4

2

4

1

Para el problema [gap8.1](#gap8.1) las soluciones obtenidas han sido las siguientes: el resultado exacto del problema es ($1133$), el resultado obtenido por el método *Greedy* propuesto por Martello-Toth es ($990$), que tras la heurística de *Búsqueda Local* toma el valor ($1100$).

##### gap12.1

```R
gap12.1 <- ReadGeneralizedAssignment("./data/assignment/gap12_1.txt")
```

```R
s <- GeneralizedAssignmentExact(gap12.1)
list(solution = matrix(s$solution, nrow = gap12.1$m, byrow = TRUE),
     objval   = s$optimum,
     slack    = s$auxiliary$primal[1:gap12.1$m])
```

$solution

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	1	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	1	0	0	1	0	1	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	1	0	0	0

0	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	0	0	0	1	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	1	0	0	1	0	0	0	0	1

0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0	0

$objval

1451

$slack

75

63

71

73

65

72

68

68

73

69

```R
s <- GeneralizedAssignmentGreedy(gap12.1)
list(solution = s$solution,
     objval   = s$objval,
     slack    = s$slack)
```

$solution

0	0	0	0	0	0	0	0	1	0	0	0	1	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	1	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	1	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1

0	1	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	1	0	0	0	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0

1	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	1	0	0	1	0	0	0

0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

$objval

1260

$slack

26

30

36

27

28

38

31

26

36

27

```R
s <- GeneralizedAssignmentGreedyLocalSearch(gap12.1)
list(solution = s$solution,
     objval   = s$objval,
     slack    = s$slack)
```

$solution

1	0	0	0	0	1	0	0	1	0	0	1	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	0	0	0	1	1	0

0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0

0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	1	0	0	0

0	1	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	1	0	1	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	1	0	0	0	0	1	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0

0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	1	0	0	1	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0

0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	1	1	0	0	0	0	0	0	0	0	0	1	0	0	0	0	1

0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	1	0	1	0	1	0	0	0	0	0	0	0	0	0	0	0	1	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0

$objval

1433

$slack

5

1

22

7

11

12

3

3

8

4

Para el problema [gap12.1](#gap12.1) las soluciones obtenidas han sido las siguientes: el resultado exacto del problema es ($1451$), el resultado obtenido por el método *Greedy* propuesto por Martello-Toth es ($1260$), que tras la heurística de *Búsqueda Local* toma el valor ($1433$).

## Conclusiones

El problema de asignación es un problema que a pesar de su simplicidad contextual, tiene grandes apliaciones en un amplio número de situaciones, es por ello que es necesario seguir estudiando el problema para buscar soluciones heurísticas eficientes y cercanas a la optimalidad. A pesar de las modelizaciones descritas en este trabajo, existen numeros ampliaciones que realizar sobre estas, tales como el aumento de la dimensionalidad del problema o la permisión de distintos valores en la demanda, al igual que ocurre con la asignación generalizada y la capacidad.

En cuanto a las modelizaciones descritas en este trabajo, estas permiten la modelización de una gran cantidad de situaciones, por lo que han sido ampliamente estudiadas en la literatura, por lo que existe una gran cantidad de heurísticas cercanas a la optimalidad, tales como las descritas en este trabajo (*Greedy* y *Búsqueda Local*) dado que cuando el tamaño del problema es inmanejable para métodos exactos, dichas heurísticas son la única manera de aproximarnos a soluciones óptimas.

## Referencias

  * [TRC13] Team, R.C., 2013. R: A language and environment for statistical computing.
  * [GP18] Sergio García Prado. Programación Entera: Heurísticas, 2018. [github.com/garciparedes/integer-programming-heuristics](https://github.com/garciparedes/integer-programming-heuristics).
  * [SA18] Jesús Sáez Aguado. Programación Entera, 2017/18. Facultad de Ciencias: Departamento de Estadística e Investigación Operativa.
  * [THBSST17] Theussl, S., Hornik, K., Buchta, C., Schwendinger, F., Schuchardt, H. and Theussl, M.S., 2017. Package ‘Rglpk’.


# CSC421 Assignment 2 - Part I  Propositional Logic (5 points) #
### Author: George Tzanetakis

This notebook is based on the supporting material for topics covered in **Chapter 7 - Logical Agents** from the book *Artificial Intelligence: A Modern Approach.* You can consult and modify the code provided in logic.py and logic.ipynb for completing the assignment questions. This part does NOT rely on the provided code so you can complete it just using basic Python.

```
Birds can fly, unless they are penguins and ostriches, or if they happen
to be dead, or have broken wings, or are confined to cages, or have their
feet stuck in cement, or have undergone experiences so dreadful as to render
them psychologically incapable of flight

Marvin Minsky
```

# Introduction - Parsing and evaluating prefix logic expressions

In this assignment your task is to incrementally create a parser and evaluator for prefix logic expressions as well as implement simple model checking. **NOTE THAT THE GRADING IN THIS ASSIGNMENT IS DIFFERENT FOR GRADUATE STUDENTS AND THEY HAVE TO DO EXTRA WORK FOR FULL MARKS**

# Question 1A (Minimum) CSC421 -  (1 point, CSC581C - 0 points)

Your first task will be to write a simple evaluator of prefix logic expressions with constants. In prefix notation the operator precedes the operands and no operands are required. For example 5+3 in prefix notation is written + 5 3 or 5 * 2 + 3 would be written + * 5 2 3 or + * 5 2 * 3 4 is equivalent to (5 * 2) + (3 * 4).

As a first step we will consider very simple expressions with one operator and two constant operands. We will use 0 for false and 1 for true. The following logical connectives should be implemented (see Figure 7.8 in your book) (notice that for now there is no negation symbol ~):

1. &    (and),
3. |    (or),
4. =>   (implication)
5. <=>  (biconditional)

Example expressions:
```
& 1 0
=> 0 1
<=> 1 1
```

Your function should take as input a string with the prefix expression and return the result of evaluating the expression (basically a 1 for true and 0 for false). You can split a string to a list using .split[' ']. For this part of the assignment you only evaluate expressions with two constant operands i.e no nested/recursive expressions.

```python
a = '& 1 0'
print(a.split(' '))
```

```python
# YOUR CODE GOES HERE
def evaluate(expression):

    expression = expression.split(' ')

    function = {
        '&': (lambda a,b : a & b),
        '|': (lambda a,b : a or b),
        '=>': (lambda a,b : (a & (a & b)) or (not a) ),
        '<=>': (lambda a,b : ((a and b) or (not a and not b)))
    }[expression[0]]

    a = bool(int(expression[1]))
    b = bool(int(expression[2]))

    return function(a,b)

print(evaluate('& 1 0'))
print(evaluate('& 0 1'))
```

# Question 1B (Minimum) (CSC421 - 1 point, CSC581C - 0 point)

Your next task is to implement variables and bindings for your propositional logic evaluator. In this version in addition to constants (0 and 1) you also can have variables which are strings with associated values provided in a dictionary. You still only consider two operands and one operator (no nesting). For example in the code below
the three expressions are equivalent. Your function should take as arguments the expression to be evaluated as a string and the dictionary with the variable bindings. In addition you need to add the ~ (not) operator. To do so for each variable in the dictionary add a not version. For example if 'a' in the dictionary has a value of 1 the '~a' in the dictionary should have a value of 0. Notice that the not symbol is part of the string and NOT separated by a space.

```python
d = {'foo': 0, 'b': 1}
print(d)
expr1 = '& 0 1'
expr2 = '& foo 1'
expr3 = '& foo ~b'
```

```python
# YOUR CODE GOES HERE
'''
Lookup takes a string and returns its boolean value.
'''
def lookup(string, var_map):
    if string in ['0', '1']: return int(string)
    if string[0] == '~': return not int(var_map[string[1:]])
    return int(var_map[string])

def evaluate_logic(expression, var_map):

    expression = expression.split(' ')

    function = {
        '&': (lambda a,b : a & b),
        '|': (lambda a,b : a or b),
        '=>': (lambda a,b : (a & (a & b)) or (not a) ),
        '<=>': (lambda a,b : ((a and b) or (not a and not b)))
    }[expression[0]]

    a = lookup(expression[1], var_map)
    b = lookup(expression[2], var_map)

    return function(a,b)

d = {'foo': 1, 'b': 0}
expr1 = '& 0 1'
expr2 = '& foo 1'
expr3 = '& foo ~b'
```

# Question 1C (Expected) 1 point

The following code is a recursive evaluator for prefix arithmetic expressions. It assumes that there are always two operands either an integer or a prefix expression starting with an operator (addition or multiplication). It is a good idea to go through this function carefully by hand to understand how the recursion works.

Informed by your understanding of the arithmetic recursive_eval function your task is to write function to implement a recursive prefix logic evaluator. Your evaluator should also support variables bindings using a dictionary as in the previous question.

Example expressions:
```
& 1 & 1 a
=> 0 & b ~alice
<=> foo 1
```

```python
def recursive_eval(l):
    head, tail = l[0], l[1:]
    if head in ['+', '*']:
        val1, tail = recursive_eval(tail)
        val2, tail = recursive_eval(tail)
        if head == '+':
            return (int(val1)+int(val2), tail)
        elif head == '*':
            return (int(val1)*int(val2), tail)
    # operator is a number
    else:
        return (int(head),tail)

def prefix_eval(input_str):
    input_list = input_str.split(' ')
    res, tail = recursive_eval(input_list)
    return res

print(prefix_eval('1'))
print(prefix_eval('+ 1 2'))
print(prefix_eval('+ 1 * 2 3'))
print(prefix_eval('+ * 5 2 * 3 + 1 5'))
```

```python
# YOUR CODE GOES HERE

def lookup(component, variables):
    if component in ['0', '1']: return int(component)
    if component[0] == '~': return int(not variables[component[1:]])
    return int(variables[component])

def evaluate(expression, variables):

    expression = expression.split(' ')

    function = {
        '&': (lambda a,b : a & b),
        '|': (lambda a,b : int(a or b)),
        '=>': (lambda a,b : int(a & (a & b) or (not a))),
        '<=>': (lambda a,b : int((a and b) or (not a and not b)))
    }[expression[0]]

    a = lookup(expression[1], variables)
    b = lookup(expression[2], variables)

    return function(a,b)

def recursive_logic_eval(l, variables):
    head, tail = l[0], l[1:]
    if head in ['=>', '&', '|', '<=>']:
        val1, tail = recursive_logic_eval(tail, variables)
        val2, tail = recursive_logic_eval(tail, variables)
        expression = "{} {} {}".format(head, val1, val2)
        return evaluate(expression, variables), tail
    # operator is a number
    else:
        return (lookup(head, variables),tail)

def prefix_logic_eval(expression, variables):
    input_list = expression.split(' ')
    res, tail = recursive_logic_eval(input_list, variables)
    return res

variables = {
    'a': 0,
    'b': 1,
    'foo': 1,
    'alice': 0
}

exp1 = '& 1 & 1 ~a'
exp2 = '& 1 & 1 a'
exp3 = '=> 1 & b ~alice'
exp4 = '<=> ~foo 0'
print(prefix_logic_eval(exp1, variables))
print(prefix_logic_eval(exp2, variables))
print(prefix_logic_eval(exp3, variables))
print(prefix_logic_eval(exp4, variables))
```

# QUESTION 1D (EXPECTED) 1 point

Using the recursive prefix evaluator you defined in the previous question
answer the following question (you will need to convert the exressions below
to prefix). You can use multiple string assignments to assemble more complicated
sentences into one big string:

Let A be the formula:

\begin{equation}
  (( p_{1} \rightarrow (p2 \land p_{3})) \land ((\neg p_{1})
  \rightarrow (p_{3} \land p_{4})))
\end{equation}

Let B be the formula:

\begin{equation}
  (( p_{3} \rightarrow (\neg p_{6})) \land ((\neg
  p_{3}) \rightarrow (p_{4} \rightarrow p_{1})))
\end{equation}

Let C be the formula:

\begin{equation}
  ((\neg(p2 \land p_{5})) \land (p2 \rightarrow p_{5}))
\end{equation}

Let D be the formula:

\begin{equation}
  (\neg (p_{3} \rightarrow p_{6}))
\end{equation}

Evaluate the formulate E:
\begin{equation}
  (( A \land (B \land C)) \rightarrow D)
\end{equation}

under the true assignment $I_{1}$, where $I_{1}(p_{1}) = I_{1}(p_{3}) = I_{1}(p_{5}) = false$
and $I_{1}(p2) = I_{1}(p_{4}) = I_{1}(p_{6}) = true$ as well as under the truth assignment
$I_{2}$, where $I_{2}(p_{1}) = I_{2}(p_{3}) = I_{2}(p_{5}) = true$ and
$I_{2}(p_{2})=I_{2}(p_{4})=I_{2}(p_{6}) = false$.

```python
# YOUR CODE GOES HERE
I1 = {
    'p1': 0,
    'p2': 1,
    'p3': 0,
    'p4': 1,
    'p5': 0,
    'p6': 1
}
I2 = {
    'p1': 1,
    'p2': 0,
    'p3': 1,
    'p4': 0,
    'p5': 1,
    'p6': 0
}

A = "& => ~p1 & p3 p4 => p1 & p2 p3"
print("A of I1:", prefix_logic_eval(A, I1))
print("A of I2:", prefix_logic_eval(A, I2))

B = "& => p3 ~p6 => ~p3 => p4 p1"
print("B of I1:", prefix_logic_eval(B, I1))
print("B of I2:", prefix_logic_eval(B, I2))

C = "& | ~p2 ~p5 => p2 p5"
print("C of I1:", prefix_logic_eval(C, I1))
print("C of I2:", prefix_logic_eval(C, I2))

D = "& ~p3 p6"
print("D of I1:", prefix_logic_eval(D, I1))
print("D of I2:", prefix_logic_eval(D, I2))

E1 = "=> & & {} {} {} {}".format(
    prefix_logic_eval(A, I1),
    prefix_logic_eval(B, I1),
    prefix_logic_eval(C, I1),
    prefix_logic_eval(D, I1)
)
E2 = "=> & & {} {} {} {}".format(
    prefix_logic_eval(A, I2),
    prefix_logic_eval(B, I2),
    prefix_logic_eval(C, I2),
    prefix_logic_eval(D, I2)
)
print("E of I1:", prefix_logic_eval(E1, I1))
print("E of I2:", prefix_logic_eval(E2, I2))
```

# QUESTION 1E (ADVANCED) 1 point

Implement inference using model-checking using your prefix recursive evaluator to decide whether a knowledge base KB entais some sentence a. To do so express the knowledge base in the prefix notation, enumerate all models for the variables in the dictionary, and check that the sentence a is true in every model in which the KB is true.

You can check the implementation to tt_entails in logic.ipynb in the aima_python repository to inform how you implement your solution. Your solution should NOT rely directly on any code in logic.py or logic.ipynb.

Check your model checking using the examples that are used in logic.ipynb to check entailment (there are a few with P and Q as variables as well as the one with A, B, C, D, E, F, G. You will need to convert these examples to prefix notation.

```python
# YOUR CODE GOES HERE

```

# QUESTION 1F (ADVANCED) (CSC421 - 0 points, CSC581C - 1 point)

Implement conversion of the prefix expressions to prefix conjuctive normal form (CNF) based on the recursive evaluator you have implemented.

```python
# YOUR CODE GOES HERE
```

# QUESTION 1E (ADVANCED) (CSC421 - 0 POINTS, CSC581C 1 point)

Based on the recursive evaluator you have implemented do a conversion of expressions in prefix notation to the infix notation of expressions supported by logic.ipynb. Provide 4 test cases that demonstrate the the conversion works by confirming that the result of your evaluator and the logic.ipynb evaluator are the same.

```python
# YOUR CODE GOES HERE
```


# Job Search Model

**Randall Romero Aguilar, PhD**

This demo is based on the original Matlab demo accompanying the  Computational Economics and Finance 2001 textbook by Mario Miranda and Paul Fackler.

Original (Matlab) CompEcon file: **demdp04.m**

Running this file requires the Python version of CompEcon. This can be installed with pip by running

    !pip install compecon --upgrade

Last updated: 2021-Oct-01

## About

Infinitely-lived worker must decide whether to quit, if employed, or search for a job, if unemployed, given prevailing market wages.

### States

-      w       prevailing wage
-     i       unemployed (0) or employed (1) at beginning of period

### Actions

-     j       idle (0) or active (i.e., work or search) (1) this period

### Parameters

| Parameter | Meaning    |
|-----------|-------------------------|
| $v$       | benefit of pure leisure |
| $\bar{w}$  | long-run mean wage |
| $\gamma$  | wage reversion rate |
| $p_0$      | probability of finding job |
| $p_1$      | probability of keeping job |
| $\sigma$   | standard deviation of wage shock |
| $\delta$   | discount factor |

# Preliminary tasks

```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from compecon import BasisSpline, DPmodel, qnwnorm, demo
```

## FORMULATION

### Worker's reward

The worker's reward is:

- $w$ (the prevailing wage rate), if he's employed and active (working)
- $u=90$, if he's unemployed but active (searching)
- $v=95$, if he's idle (quit if employed, not searching if unemployed)

```python
u     =  90
v     =  95

def reward(w, x, employed, active):
    if active:
        return w.copy() if employed else np.full_like(w, u)  # the copy is critical!!! otherwise it passes a pointer to w!!
    else:
        return np.full_like(w, v)
```

### Model dynamics

#### Stochastic Discrete State Transition Probabilities

An unemployed worker who is searching for a job has a probability $p_0=0.2$ of finding it, while an employed worker who doesn't want to quit his job has a probability $p_1 = 0.9$ of keeping it. An idle worker (someone who quits or doesn't search for a job) will definitely be unemployed next period. Thus, the transition probabilities are
\begin{align}
    q = \begin{bmatrix}1-p_0 &p_0\\1-p_1&p_1\end{bmatrix},&\qquad\text{if active} \\
      = \begin{bmatrix}1 & 0\\1 &0 \end{bmatrix},&\qquad\text{if iddle}
\end{align}

```python
p0    = 0.20
p1    = 0.90

q = np.zeros((2, 2, 2))
q[1, 0, 1] = p0
q[1, 1, 1] = p1
q[:, :, 0] = 1 - q[:, :, 1]
```

#### Stochastic Continuous State Transition
Assuming that the wage rate $w$ follows an exogenous Markov process
\begin{equation}
    w_{t+1} = \bar{w} + \gamma(w_t − \bar{w}) + \epsilon_{t+1}
\end{equation}

where $\bar{w}=100$ and $\gamma=0.4$.

```python
wbar  = 100
gamma = 0.40
def transition(w, x, i, j, in_, e):
    return wbar + gamma * (w - wbar) + e
```

Here, $\epsilon$ is normal $(0,\sigma^2)$ wage shock, where $\sigma=5$. We discretize this distribution with the function ```qnwnorm```.

```python
sigma = 5
m = 15
e, w = qnwnorm(m, 0, sigma ** 2)
```

### Approximation Structure

To discretize the continuous state variable, we use a cubic spline basis with $n=150$ nodes between $w_\min=0$ and $w_\max=200$.

```python
n = 150
wmin = 0
wmax = 200
basis = BasisSpline(n, wmin, wmax, labels=['wage'])
```

## SOLUTION

To represent the model, we create an instance of ```DPmodel```. Here, we assume a discout factor of $\delta=0.95$.

```python
model = DPmodel(basis, reward, transition,
                i =['unemployed', 'employed'],
                j = ['idle', 'active'],
                discount=0.95, e=e, w=w, q=q)
```

Then, we call the method ```solve``` to solve the Bellman equation

```python
S = model.solve(show=True)
S.head()
```

    Solving infinite-horizon model collocation equation by Newton's method
    iter change       time
    ------------------------------
       0       2.1e+03    0.1516
       1       3.5e+01    0.3620
       2       1.9e+01    0.5605
       3       1.4e+00    0.7629
       4       2.8e-12    1.0018
    Elapsed Time =    1.00 Seconds

      wage
      i
      value
      resid
      j*
      value[idle]
      value[active]

      wage

      unemployed
      0.000000
      0.000000
      0
      1911.101186
      0.000000e+00
      idle
      1911.101186
      1906.101213

      0.133422
      0.133422
      0
      1911.101997
      -1.405169e-10
      idle
      1911.101997
      1906.102026

      0.266845
      0.266845
      0
      1911.102810
      -1.452918e-10
      idle
      1911.102810
      1906.102841

      0.400267
      0.400267
      0
      1911.103625
      -5.434231e-11
      idle
      1911.103625
      1906.103656

      0.533689
      0.533689
      0
      1911.104440
      9.526957e-11
      idle
      1911.104440
      1906.104473

### Compute and Print Critical Action Wages

```python
def critical(db):
    wcrit = np.interp(0, db['value[active]'] - db['value[idle]'], db['wage'])
    vcrit = np.interp(wcrit, db['wage'], db['value[idle]'])
    return wcrit, vcrit

wcrit0, vcrit0 = critical(S.loc['unemployed'])
print(f'Critical Search Wage = {wcrit0:5.1f}')

wcrit1, vcrit1 = critical(S.loc['employed'])
print(f'Critical Quit Wage   = {wcrit1:5.1f}')
```

    Critical Search Wage =  93.8
    Critical Quit Wage   =  79.4

### Plot Action-Contingent Value Function

```python
vv = ['value[idle]','value[active]']
fig1 = plt.figure(figsize=[12,4])

# UNEMPLOYED
demo.subplot(1,2,1,'Action-Contingent Value, Unemployed', 'Wage', 'Value')
plt.plot(S.loc['unemployed',vv])
demo.annotate(wcrit0, vcrit0, f'$w^*_0 = {wcrit0:.1f}$', 'wo', (5, -5), fs=12)
plt.legend(['Do Not Search', 'Search'], loc='upper left')

# EMPLOYED
demo.subplot(1,2,2,'Action-Contingent Value, Employed', 'Wage', 'Value')
plt.plot(S.loc['employed',vv])
demo.annotate(wcrit1, vcrit1, f'$w^*_0 = {wcrit1:.1f}$', 'wo',(5, -5), fs=12)
plt.legend(['Quit', 'Work'], loc='upper left')
```

### Plot Residual

```python
S['resid2'] = 100 * (S['resid'] / S['value'])
fig2 = demo.figure('Bellman Equation Residual', 'Wage', 'Percent Residual')
plt.plot(S.loc['unemployed','resid2'])
plt.plot(S.loc['employed','resid2'])
plt.legend(model.labels.i)
```

## SIMULATION

### Simulate Model

We simulate the model 10000 times for a time horizon $T=40$, starting with an unemployed worker ($i=0$) at the long-term wage rate mean $\bar{w}$. To be able to reproduce these results, we set the random seed at an arbitrary value of 945.

```python
T = 40
nrep = 10000
sinit = np.full((1, nrep), wbar)
iinit = 0
data = model.simulate(T, sinit, iinit, seed=945)
```

```python
data.head()
```

      time
      _rep
      i
      wage
      j*

      0
      0
      0
      unemployed
      100.0
      active

      1
      0
      1
      unemployed
      100.0
      active

      2
      0
      2
      unemployed
      100.0
      active

      3
      0
      3
      unemployed
      100.0
      active

      4
      0
      4
      unemployed
      100.0
      active

### Print Ergodic Moments

```python
ff = '\t{:12s} = {:5.2f}'

print('\nErgodic Means')
print(ff.format('Wage', data['wage'].mean()))
print(ff.format('Employment', (data['i'] == 'employed').mean()))
print('\nErgodic Standard Deviations')
print(ff.format('Wage',data['wage'].std()))
print(ff.format('Employment', (data['i'] == 'employed').std()))
```

    Ergodic Means
    	Wage         = 100.02
    	Employment   =  0.58

    Ergodic Standard Deviations
    	Wage         =  5.37
    	Employment   =  0.49

```python
ergodic = pd.DataFrame({
    'Ergodic Means' : [data['wage'].mean(), (data['i'] == 'employed').mean()],
    'Ergodic Standard Deviations': [data['wage'].std(), (data['i'] == 'employed').std()]},
    index=['Wage', 'Employment'])

ergodic.round(2)
```

      Ergodic Means
      Ergodic Standard Deviations

      Wage
      100.02
      5.37

      Employment
      0.58
      0.49

### Plot Expected Discrete State Path

```python
data.head()
```

      time
      _rep
      i
      wage
      j*

      0
      0
      0
      unemployed
      100.0
      active

      1
      0
      1
      unemployed
      100.0
      active

      2
      0
      2
      unemployed
      100.0
      active

      3
      0
      3
      unemployed
      100.0
      active

      4
      0
      4
      unemployed
      100.0
      active

```python
data['ii'] = data['i'] == 'employed'

fig3 = demo.figure('Probability of Employment', 'Period','Probability')
plt.plot(data[['ii','time']].groupby('time').mean())
```

### Plot Simulated and Expected Continuous State Path

```python
subdata = data[data['_rep'].isin(range(3))]

fig4 = demo.figure('Simulated and Expected Wage', 'Period', 'Wage')
plt.plot(subdata.pivot('time', '_rep', 'wage'))
plt.plot(data[['time','wage']].groupby('time').mean(),'k--',label='mean')
```

```python
#demo.savefig([fig1,fig2,fig3,fig4])
```

_build/jupyter_execute/notebooks/dp/04 Job Search Model.ipynb


# 確率・統計の基礎

本節では機械学習で用いる確率論、統計学の概念や用語を解説します。

世の中には「ランダム」に起こる出来事、もしくは背景のメカニズムがわからないため「ランダム」として扱わざるを得ない出来事が存在します。そのようなランダムな出来事を理論的に扱うには、出来事そのものや、ある出来事と別の出来事の関連を数学的に記述できなければなりません。確率論ではランダムな出来事のことを**事象 (event)**と呼びます（厳密な定義は本資料では省略します）。

本節では、まず事象を記述する道具として確率分布を導入し、それに関連する概念（周辺確率、条件付き確率、確率変数の独立）を解説します。さらにこれらの概念を用いてベイズの定理を説明します。ベイズの定理により、ある出来事が観測された時に、その原因となる出来事が起こった確率の計算ができるようになります。
次に、確率分布の中から観測データに適合した最適な確率分布を選択する方法である、最尤推定と事後分布最大化推定（MAP 推定）を紹介します。
機械学習の文脈では、これらは訓練モデルの「最適な」パラメータを決定することに対応します。
最後に、機械学習で頻繁に利用される統計学の用語（平均、分散、標準偏差、相関係数など）を解説します。

## 確率・統計と機械学習の関係

機械学習システムが学習に用いるのは限られた数の観測データですが、多くの機械学習タスクでやりたいことは、与えられた観測データの背後にある普遍性や法則を捉え、将来の出来事に対し予測を行えることです。機械学習ではそれを達成するために統計学の考え方を利用します。

統計学では、ある集団すべてについて調査ができない時、集団からランダムサンプリングを行い元の集団の性質を推定します。手元にある観測データが、何らかの法則に従って得られる確率的なサンプルであると考えることで、機械学習と統計学がつながります。
統計学を利用することで、あるデータが未知のデータ源から発生しやすいか、データが外れ値かどうか、どういった方法でモデルを学習させればよいか、といった問題を客観的に判断することができるようになります。また、学習させたモデルの性能に対して理論的な保証を与えることも統計学により可能となります。
このような、機械学習の統計学としての側面を強調する場合、機械学習を特に「統計的機械学習」と呼びます。

## 確率変数と確率分布

現代数学で広く利用されている「確率」の概念を定式化するには様々な準備が必要になるため、この資料では「確率」という言葉を数学的に厳密には定義しません。代わりに次のように考えます。ある対象としている現象の中で、様々な事象があり得るとき、それぞれの事象ごとに、それが「どの程度起きそうか」という度合いを考えます。確率とはその度合いのこととします。そして、その確率に従って、色々な値をとりうる**確率変数（random variable）**を考えます。確率変数は、名前に「変数」とついていますが、**「事象」を「数値」に変換する関数**と考えると理解しやすくなります。例えば、「コインを投げて表が出る」という「事象」を、「1」という「数値」に変換し、「コインを投げて裏が出る」という「事象」を、「0」という「数値」に変換する関数を考えると、これは「1」か「0」という値のどちらかをとりうる確率変数だ（[注釈1](#note1)）ということになります。

それでは、確率的現象の具体例を考えてみます。ある歪んだサイコロがあり、「サイコロを投げて $x$ という目が出た」という事象（[注釈2](#note2)）を、$x$ という数値に対応させる確率変数 $X$ があるとします。そして、この確率変数がとりうる全ての値が、それぞれどのような確率で出現するかを表した以下のような表があります。

| 確率変数 $X$ の値 | その値をとる確率 |
|:-----------------:|:----------------:|
| 1                 | $0.3$            |
| 2                 | $0.1$            |
| 3                 | $0.1$            |
| 4                 | $0.2$            |
| 5                 | $0.1$            |
| 6                 | $0.2$            |

このような表を**確率分布 (probability distribution)** と呼びます。確率分布には重要な制約があり、「確率変数がとりうるあらゆる値の確率をすべて足すと和が必ず $1$ になること」及び「全ての確率は $0$ 以上の値であること」の両者を常に満たします。上の表の左の列に並ぶ数値を**実現値**と呼び、小文字の $x$ で表します。そして、右の列に並ぶそれぞれの $x$ に対応する確率を $p(x)$ と書くことにします。すなわち上の表から $p(1) = 0.3$ , $p(2) = 0.1$ , $\ldots$ です。この表記を用いると、確率分布が持つ2つの制約は、以下のように表せます。

$$
\begin{align}
\sum_x p(x) &= 1 \\
\forall x, \ p(x) & \geq  0
\end{align}
$$

ここで、 $\sum_x$ は全てのありうる $x$ の値にわたる和を表し、例えば上のサイコロの例では、 $\sum_{x=1}^6$ と同じことを意味します。 $\forall x$ は、あり得る $x$ の値すべてにおいて、右の条件（ $p(x) \geq 0$ ）が成り立つ、ということを意味しています。

$p(1) = 0.3$ というのは、確率変数 $X$ が $1$ という値をとる確率ということですが、これを $p(X = 1) = 0.3$ とも書きます。前述の $p(x)$ は、確率変数 $X$ の存在を暗に仮定し、 $p(X = x)$ を簡単に表記したもの、すなわち $X$ という確率変数がある値 $x$ をとる確率と考えることができます。一方、 $p(X)$ と確率変数だけを引数にとる場合、これは上の表のような確率分布を意味します。

## 同時分布・周辺確率

前節では、1 つの確率変数について、その分布（確率分布のこと）とは何かと、分布が持つ制約について説明しました。この節では、複数の確率変数が登場する場合について考えます。

まず具体例を用いて考えてみます。ここに 2 つのサイコロがあり、それぞれのサイコロの出目を 2 つの確率変数 $X, Y$ で表します。この 2 つのサイコロを同時に振って、1 つ目のサイコロが $x$ という値をとり、2 つ目のサイコロが $y$ という値をとったという事象の確率は、以下のように書き表します。

$$
p(X = x, Y = y)
$$

 $x, y$ はいずれも $1, 2, 3, 4, 5, 6$ の6つの数字のどれかです。例えば、「3」と「5」の目が出る、という事象が起こる確率は

$$
p(X = 3, Y = 5)
$$

と表されます。このように、 $X = 3$ となる**かつ** $Y = 5$ となる、といった複数の条件を指定したときに、それらが全て同時に成り立つ確率のことを、**同時確率（joint probability）**と呼びます。

では次に、この 2 つのサイコロを別々に見てみましょう。例えば、「1 つ目のサイコロが $3$ の目を出した」という事象が起こる確率 $p(X = 3)$ は、1つ目のサイコロが $3$ **かつ** 2 つ目のサイコロが $1$ のとき／ $2$ のとき／ $3$ のとき／…／ $6$ のとき、の6つのパターンが発生する確率を全て足したものになります。つまり、

$$
p(X = 3) = \sum_y p(X = 3, Y = y)
$$

と書けます。このとき、 $\sum_y$ は「 $Y$ のとり得るあらゆる値 $y$ についての和」という意味です。これを、「（2 つ目のサイコロの目がなんであれ） 1 つ目のサイコロの目が $x$ である確率」と一般化すると、以下のようになります。

$$
p(X = x) = \sum_y p(X = x, Y = y)
$$

同様に、「（1つ目のサイコロの目がなんであれ）2 つ目のサイコロの目が $y$ である確率」は、1つ目のサイコロについてあり得る値すべての確率の和を取れば良いので、

$$
p(Y = y) = \sum_x p(X = x, Y = y)
$$

となります。このように、同時確率が与えられたとき、着目していない方の確率変数がとり得る全ての値について同時確率を計算しその和をとることを**周辺化 (marginalization)** と呼び、結果として得られる確率を**周辺確率 (marginal probability)** と呼びます。
また、周辺確率をその着目している確率変数がとり得る全ての値について並べて一覧にしたものが**周辺確率分布 (marginal probability distribution)** です。
さらに、上の例のように2つの確率変数の同時確率を考えるとき、とり得る全ての組み合わせの確率を一覧にしたものが、**同時分布 (joint distribution)** です。

ここで、2 つのサイコロの同時分布の表は大きくなってしまうので、より簡単な例として、表が出る確率と裏が出る確率が異なる 2 つのコインを考えてみましょう。この 2 つのコインを同時に投げたときの表裏の組み合わせについての同時分布が、以下のようになったとします。

| $\ $   | Y = 表 | Y = 裏 |
|:------:|:------:|:------:|
| X = 表 | 1 / 5  | 2 / 5  |
| X = 裏 | 1 / 5  | 1 / 5  |

ここで、1 つ目のコインの表裏を表す確率変数を $X$ 、2 つ目のコインの表裏を表す確率変数を $Y$ としています。表の中身がどういう意味を持っているか確認してください。2 つのコインが両方表になる確率は $p(X = 表, Y = 表) = 1 / 5$ となっています。他のマスに書かれている同時確率の値の意味も確認してください。

では、この表の中の数字を、行ごとに合計してみましょう。1 行目は、

$$
p(X = 表, Y = 表) + p(X = 表, Y = 裏) = 1 / 5 + 2 / 5 = 3 / 5
$$

です。
これは、 $\sum_y p(X = 表, Y = y)$ （[注釈3](#note3)）を計算していることになるので、**周辺化によって** $p(X = 表)$ **という周辺確率を求めていることに相当します。**

同様に、1 列目の値を合計してみると、今度は $\sum_x p(X = x, Y = 表)$ （[注釈4](#note4)） を計算することに相当し、これは周辺化によって $P(Y = 表)$ という周辺確率を計算していることになります。

こうして計算される周辺確率を、上の同時分布の表に書き入れてみます。

| $\ $   | Y = 表 | Y = 裏 | p(X)  |
|:------:|:------:|:------:|:-----:|
| X = 表 | 1 / 5  | 2 / 5  | 3 / 5 |
| X = 裏 | 1 / 5  | 1 / 5  | 2 / 5 |
| p(Y)   | 2 / 5  | 3 / 5  |       |

このように、周辺確率はしばしば同時分布表の周辺に記述されます。

## 条件付き確率

前節では、複数の確率変数を同時に考える方法として、同時確率および同時分布という概念と、同時確率と一つ一つの確率変数のみに着目した際の確率（周辺確率）の間の関係を、周辺化という計算で説明しました。
本節では、「とある条件下での着目事象の確率」を考える**条件付き確率 (conditional probability)** という概念を説明します。
これまでの節で扱っていた確率では、まず対象とする現象について、考えうる全ての事象を考え、そのうち対象とする事象が起きる確率はいくらか、を考えていました。
一方、条件付き確率は、それらの考えうる事象のうち、特定の条件を満たした事象のみをまず抜き出し、その中で、さらに着目する特定の事象が起きる確率を考えるためのものです。

例えば、あるお店にやってきた人（以下、客）が傘を持っているときに $1$ 、持っていなかったときに $0$ をとる確率変数 $X$ を考えます。
また、ある客がやってきたときに外で雨が降っていたときに $1$ 、降っていなかったときに $0$ をとる確率変数 $Y$ を考えます。
客が傘を持っていたかと、その瞬間に雨が降っていたかどうか、という 2 つの情報が組となったデータが 16 個あるとします。
16 人の客を観測してデータを集めた、ということです。
それは以下の様なものでした。

| 傘 ( $X$ ) | 雨 ( $Y$ ) |
|:--------:|:-------:|
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 0 | 1 |
| 0 | 1 |
| 0 | 1 |
| 1 | 0 |
| 0 | 0 |
| 0 | 0 |
| 0 | 0 |
| 0 | 0 |
| 0 | 0 |
| 0 | 0 |

これらのデータから、確率変数 $X, Y$ の同時分布表を作成すると、以下のようになります。この表の作り方は、例えば表の左上のマスは $X = 0$ かつ $Y = 0$ となっているデータが　16 個中何個あるかを数えて、その割合を記入すればよく、他のマスについても同様となります。

| $\ $  | Y = 0  | Y = 1  |
|:-----:|:------:|:------:|
| X = 0 | 6 / 16 | 3 / 16 |
| X = 1 | 1 / 16 | 6 / 16 |

では、「客が傘を持っていた（ $X = 1$ ）」という**条件の下で**、「そのとき雨が降っていなかった（ $Y = 0$ ）」という確率は、どのようになるでしょうか？

まず、「客が傘を持っていた（ $X = 1$ ）」という状況下のデータだけを、上の同時分布表から抜き出してきましょう。

| 傘 ($X$) | 雨 ($Y$) |
|:--------:|:-------:|
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 1 | 1 |
| 1 | 0 |

全部で 7 個のデータがあり、この中で「雨が降っていなかった（ $Y = 0$ ）」となっているデータは 1 個です。すなわち、「客が傘を持っていたという**条件の下で**、そのとき雨が降っていなかった確率」は $1 / 7$ となります。これを、以下のように書きます。

$$
p(Y = 0 | X = 1) = \frac{1}{7}
$$

これが**条件付き確率**と呼ばれるもので、条件となる事象を縦棒 $|$ の後に書き、その条件下で注目している事象を縦棒 $|$ の前に書きます。同様に、「客が傘を持っていたという**条件の下で**、そのとき雨が降っていた確率」は、上の表から $6 / 7$ なので、以下が成り立ちます。

$$
p(Y = 1 | X = 1) = \frac{6}{7}
$$

これらの 2 つの確率が、それぞれ $p(Y = 0, X = 1) = 1 / 16$ , $p(Y = 1, X = 1) = 6 / 16$ という同時確率とは異なっていることを、上の同時分布表と見比べて確認してみましょう。
「客が傘を持っていて**かつ**外で雨が降っている」という同時確率は $6 / 16$ となっていますが、「客が傘を持っているという**条件の下で**外で雨が降っている」という条件付き確率は $6 / 7$ でした。
前者は「客が傘を持っておらず外で雨が降っていた」「客が傘を持っておらず外で雨が降っていなかった」「客が傘を持っていたが外で雨が降っていなかった」を含むあり得る全ての事象を考えた上で、その中で対象としている事象「客が傘を持っていて外で雨が降っている」が起こる確率を意味しています。一方で、後者の条件付き確率は、今条件となっている事象「客が傘を持っている」が成り立っている事象だけをまず対象とし、その中で着目している事象「外で雨が降っている」が起こる確率を考えます。それぞれの確率の値を計算する際の分母が異なっていることに注目してください。

また、上記の 2 つの条件付き確率を足すと、 $1$ になります。「ある条件下で」という制約された世界で、対象とする確率変数がとり得る値全ての確率を並べて一覧したものは**条件付き分布（conditional distribution）**と呼ばれ、それらの確率を全て足すと必ず $1$ になります。上の例では、以下の計算で確かめられます。

$$
p(Y = 0 | X = 1) + p(Y = 1 | X = 1) = \frac{1}{7} + \frac{6}{7} = 1
$$

ここで、確率変数 $X$ がとりうる実際の値のうちいずれかを表す文字として $x$ を、確率変数 $Y$ がとりうる実際の値のうちいずれかを表す文字として $y$ を用いると、一般的に、条件付き確率は以下のように定義されます。

$$
p(Y = y | X = x)
= \frac{p(X = x, Y = y)}{p(X = x)}
$$

このとき、条件付き分布が満たす条件は以下のように表せます。

$$
\sum_y p(Y = y | X = x) = 1
$$

ここで、 $\sum_x P(Y = y | X = x)$ は必ずしも1にならないということに注意してください。

では、上の条件付き確率の定義を用いて、同時確率と周辺確率から条件付き確率を、再度計算してみましょう。

$$
\begin{align}
p(Y = 0 | X = 1)
&= \frac{p(X = 1, Y = 0)}{p(X = 1)} \\
&= \frac{\frac{1}{16}}{\frac{7}{16}} \\
&= \frac{1}{7}
\end{align}
$$

確かに、上で別の方法で求めた条件付き確率と一致しました。

さて、上で触れた、同時確率と条件付き確率の関係について再度考えてみましょう。前述の「条件付き確率の定義」の式を変形すると、以下のようになります。以降では、簡単のために、確率変数の記述を省き、 $p(X = x)$ を $p(x)$ と表記します。確率変数 $X$ によって $x$ という値に対応付けられている事象が生じる確率、という意味です。同様に、 $p(Y = y)$ を $p(y)$ と表記します。

$$
p(x, y) = p(y | x) p(x)
$$

同時確率は、条件付き確率に、その条件となっている事象が生じる確率（周辺確率）を掛け合わせることで得られることが分かります。

今回の例では、「雨が降っているかどうか」によって「客が傘を持っていたかどうか」は影響を受けるため、2つの確率変数 $Y, X$ は**従属 (dependent)**、と言います。一方、例えば「客が来たとき雨が降っていたかどうか」と「私が今お腹が空いているかどうか」が全く関係していないとします。後者について、お腹が空いているという事象を $1$ に、お腹が空いていないという事象を $0$ に対応付ける確率変数 $Z$ を導入すると、$Y$ と $Z$ が今**独立 (independent)**であるという状況です。このとき、$Z$ の実現値を $z$ として、条件付き確率 $p(y | z)$ は、$Y$ がどうなるかが $Z$ がどうなるかに全く関係がないことから、 $p(y)$ と等しくなります。条件付けたとしても、条件付けなかったとしても、結果が変わらないためです。これを用いると、 $y$ と $z$ の同時確率は以下のように変形できます。

$$
\begin{align}
p(y, z) &= p(y | z) p(z) \\
p(y, z) &= p(y) p(z)
\end{align}
$$

 $Y$ と $Z$ が独立であることから、 $p(y | z) = p(y)$ となることを用いました。この式から、**2つの確率変数が独立であるとき、同時確率はそれぞれの周辺確率の積で書ける**ということが言えます。

## ベイズの定理

前節で説明した条件付き確率は、ある前提が成り立っている状況下で着目する事象が生じる確率を記述するためのものでした。これは、「ある"原因"が生じたという条件の下で、ある"結果"が生じる確率」を考えていると捉えることができます。では、逆に**「ある"結果"が観測されたというとき、ある事象が"原因"である確率」**を考えるにはどうしたら良いでしょうか。今、原因となり得る事象が確率変数 $X$ で表され、結果となり得る事象が確率変数 $Y$ で表されるとします。このとき、原因として実際に $x$ が観測されたもとで結果が $y$ となる確率が条件付き確率

$$
p(Y = y | X = x)
$$

で表されました。前節の定義から、これに原因 $x$ がそもそも発生する確率 $p(X = x)$ を掛け合わせると、同時確率 $p(Y = y, X = x)$ になります。すなわち、

$$
p(Y = y | X = x) p(X = x) = p(Y = y, X = x) \hspace{2em} \cdots (1)
$$

です。では、結果が $y$ であったという観測の下で、原因が $x$ である確率はどのように表されるでしょうか。これは上の「条件の事象」と「着目する事象」を入れ替えた条件付き確率として、

$$
p(X = x | Y = y)
$$

と書くことができます。 $y$ という結果が観測されたもとで、原因が $x$ であった確率という意味です。これに結果 $y$ がそもそも発生する確率 $p(Y = y)$ をかけ合わせると、条件付き確率の定義から

$$
p(X = x| Y = y) p(Y = y) = p(X = x, Y = y) \hspace{2em} \cdots (2)
$$

が導かれます。ここで、式(1)と式(2)の右辺が同じになっていることに着目します。すると、式(1)と式(2)の左辺同士を等号で結ぶことができ、

$$
p(Y = y | X = x) p(X = x) = p(X = x | Y = y) p(Y = y)
$$

が成り立つことが分かります。この式を変形して、求めたい「結果 $y$ が生じたという**条件の下で**原因が $x$ である確率」を左辺に残すと、

$$
p(X = x | Y = y) = \frac{p(Y = y | X = x) p(X = x)}{p(Y = y)}
$$

が導かれます。これを**ベイズの定理（Bayes' theorem）**と言います。これによって、ある結果が観測されたときに、ある事象が原因である確率を、

- 原因として考えている事象が（結果と関係なく）そもそも生じる確率（ $p(X = x)$ ）
- 結果として観測した事象が（原因に関係なく）そもそも生じる確率（ $p(Y = y)$ ）
- 考えている原因が実際に生じたという下で観測された結果が生じる確率（ $p(Y = y | X = x)$ ）

の3つから、求めることができます。ベイズの定理に登場する左辺の条件付き確率と右辺の分子にある周辺確率には、特別な呼び方があります。まず右辺の分子にある周辺確率（上式では $p(X = x)$ ）は、「結果」が観測する前（事前）に、原因 $x$ がそもそも生じる確率を表すので、**事前確率（prior probability)**と呼ばれます。一方、左辺の条件付き確率（上式では $p(X = x | Y = y)$ ）は、ある「結果 $y$」が観測されたという条件の下で（事後に）、原因 $x$ が生じていたという確率を表すので、**事後確率（posterior probability）**と呼ばれます。

ベイズの定理の応用事例として、スパムメールフィルターがあります。まず、 $N$ 個の着目する単語 $w_i (i=1, \dots, N)$ を決めます（"sale", "buy", "free", ...など）。そして、メールにある単語 $w_i$ が含まれるとき $1$ となり、含まれないとき $0$ となる確率変数を $W_i$ とし、「メールにある単語 $w_i$ が含まれる確率」を $p(W_i = 1)$ とします。 次に、メールがスパムメールであるとき $1$ となり、スパムメールでないとき $0$ となる確率変数を $Y$ とし、「スパムメールの存在確率」を $p(Y = 1)$ とします。このとき、「あるメールがスパムメールであったという**状況の下で**単語 $w_i$ が含まれていた」という確率は $p(W_i = 1 | Y = 1)$ となります。すると、例えば $w_i$ と $w_j$ が両方含まれていた場合は、同時確率 $p(W_i = 1, W_j = 1 | Y = 1)$ を考えることになりますが、各単語が独立に現れると仮定すると、これは $p(W_i = 1 | Y = 1) p(W_j = 1 | Y = 1)$ と分解できます。ここで、世の中にある大量のメールを集めて、各単語がどのくらいの確率で現れるかを調べると、 $p(W_i = 1) \ (i=1,\dots,N)$ が求まります。また、集めたメールからスパムメールだけを抜き出します。すると、メールがスパムメールである確率 $p(Y = 1)$ が求まります。さらに、それらのスパムメール中に含まれる単語を調べ、どの単語がどういう確率で出現しているかを、着目している全単語について求めると、 $p(W_i = 1 | Y = 1) \ (i=1, \dots, N)$ が全て求まります。これらを用いると、「あるメールに単語 $w_i, w_j, w_k, \dots$ らが含まれているとき、そのメールがスパムメールである確率」を以下のように計算できます。

$$
p(Y = 1 | W_i = 1, W_j = 1, W_k = 1, \dots ) = \frac{p(W_i = 1, W_j = 1, W_k = 1, \dots | Y = 1) p(Y = 1)}{p(W_i = 1, W_j = 1, W_k = 1, \dots )}
$$

## 尤度と最尤推定

これまでの節では、「**確率変数がとりうる様々な値に確率を対応させる**」確率分布は、表の形で表されていました。ここで、確率変数 $X$ が $x$ という値をとる確率 $p(X = x)$ を、表の代わりに、あるパラメータ $\theta$ で特徴づけられた関数 $f(x; \theta)$ によって表してみます。この関数は、「**確率変数** $X$ **がとりうる様々な値** $x$ **に確率を対応させる**」ので、 $X$ の確率分布を表しています。このような関数は**確率モデル（probabilistic model）**とも呼ばれ、特にパラメータによって形状が決定される関数を用いる場合は**パラメトリックモデル（parametric model）**と呼ばれます。

このような確率モデルのパラメータをうまく決定することで、データの分布を表現できれば、未知のデータに対してもそれがどのくらいの確率で発生するのかといった予測が可能となったり、便利です。推定方法には様々なものがあり、どうやって推定するかは推定する人の自由ですが、本節では最も一般的な方法の一つである最尤推定を説明します。最尤推定は確率論から正当化できるという長所があります。

さて、確率モデルがどのくらい実際の観測データに即しているかを**尤度（ゆうど）（likelihood）**と言い、観測されたデータをモデルに入力し、その出力をかけ合わせたもので定義されます。

例えば、 $N$ 個のデータ $x_i \ (i=1,\dots,N)$ が独立に観測されたとき、この観測データの下での確率モデル $f(x; \theta)$ の**尤度**は

$$
L(\theta) = \prod_{i=1}^N f(x_i; \theta)
$$

と計算されます。与えられたデータから、「どのようなデータがどのくらいよく観測されるか」を表す確率分布を推定（[注釈5](#note5)）するために、予め用意した確率モデルのパラメータを、それらの観測データの下での**尤度が最大**になるように決定する方法を、**最尤（さいゆう）推定（maximum likelihood estimation）**と言います。

上式の $\prod$ という記号は、 $\sum$ の掛け算版で、全ての値を掛け合わせるという意味です。複数データに対する尤度は、 $1$ より小さな値の積となるため、結果は非常に小さな数になり、コンピュータでこの計算を行う際にはアンダーフローという問題が発生しやすくなります。また尤度を最大化したい場合、積の形の式の最大化は難しいことが知られています。そこで尤度の対数をとった**対数尤度（log likelihood）**を考えます。 $y = \log(x)$ は単調増加であるため、対数をとる前後で大小関係は変わりません。そのため対数尤度を最大にするときの $\theta$ は、元の尤度も最大にします。

$$
\log L(\theta) = \sum_{i=1}^N \log f(x_i; \theta)
$$

よって、この対数尤度を最大化するパラメータ $\theta$ を求めることができれば、その値が観測データ $x_i (i=1,\dots,N)$ の分布を最もよく表現する確率モデルのパラメータとなります。

### 例：コイントスのパラメータ推定

ここで、具体例として、コインの表・裏が出る確率を推定する問題を考えてみます。コインを投げて「表が出る」事象を $1$ に、「裏が出る」事象を $0$ に対応付ける確率変数を $X$ とします。このとき、 $p(X)$ をシンプルな確率モデル

$$
f(x; \theta) = \theta^x (1 - \theta)^{1 - x}
$$

で表現することにします（[注釈6](#note6)）。これは $x = 1$ のとき、すなわちコインが表であるとき $\theta$ をそのまま返し、 $x = 0$ のとき、すなわちコインが裏であるとき $1 - \theta$ を返すような関数となっています。ただし、 $0 \leq \theta \leq 1$ を満たすこととします。今、コインを $10$ 回投げ、以下の観測結果が得られたとします。

| $\ $ | 1回目 | 2回目 | 3回目 | 4回目 | 5回目 | 6回目 | 7回目 | 8回目 | 9回目 | 10回目 |
|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|
| X | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |

これらの観測データの下での確率モデルの尤度は

$$
\begin{aligned}
L(\theta) &=
\theta \cdot
(1 - \theta) \cdot
\theta \cdot
\theta \cdot
\theta \cdot
(1 - \theta) \cdot
(1 - \theta) \cdot
\theta \cdot
(1 - \theta) \cdot
(1 - \theta) \\
&= \theta^{5} \cdot (1 - \theta)^{5}
\end{aligned}
$$

です。対数尤度は、

$$
\log L(\theta) = 5 \log \theta + 5 \log \left( 1 - \theta \right)
$$

となります。これが最大となるときの $\theta$ は、この式を $\theta$ で微分した式を $0$ とおいて解析的に解けばよく、

$$
\begin{align}
\frac{5}{\theta} - \frac{5}{\left( 1 - \theta \right)} &= 0 \\
\therefore \theta = \frac{1}{2}
\end{align}
$$

と決定します。これが最尤推定です。

## 事後確率最大化推定（MAP推定）

前節で解説した最尤推定は、多くの場合で有用ですが、求めるパラメータに何らかの**事前知識**があっても、それを活かすことができません。そのため、サンプルサイズが小さい場合に、実際にはほとんどあり得ない結果が求まることがあります。具体例を見てみましょう。

再び、コインの表・裏が出る確率を推定する例を考えます。コインを $5$ 回投げたところ、**たまたま** ${\bf 5}$ **回とも表が出た**とします。この場合、前節と同じ確率モデルを用いて尤度を計算し、最尤推定でモデルのパラメータを決定すると、 $0 \leq \theta \leq 1$ を満たしながら、今回の観測データの下での尤度 $\theta^5$ を最大にする $\theta$ は $1$ となります。これは「表が出る確率が $1$ 」すなわち**「裏面が出る確率は** $0$**」**という推定結果が得られてしまったことを意味します。もし「裏が出る確率も $0$ よりは大きいだろう」という事前知識を持っておりそれを活用できれば、今回のような場合でもより良い推定ができそうです。

事前知識も考慮しながら、観測データに基づいて確率モデルのパラメータを推定する方法には、**事後確率最大化（maximum a posteriori, 以降 MAP）推定**があります。MAP 推定でも、対象のデータの確率分布を表す確率モデルのパラメータを、データを利用して決定しますが、データだけでなく**パラメータも確率変数だと考える**点が最尤推定とは異なっています。パラメータを確率変数 $\Theta$ とすると、このパラメータがどのような値をとるかを表す分布 $p(\Theta)$ が導入できます。これを**事前分布（prior distribution）**と呼び、ここに「求めたいパラメータに対する事前知識」を反映させることができます。ここで、 $\Theta = \theta$ となる確率 $p(\Theta = \theta)$（以降は $p(\theta)$ と略記）をデータの確率モデル $f(x; \theta)$ とは別の確率モデル $g(\theta; \beta)$ で表せば、 $\beta$ を人為的に与えるか、なんらかの方法で決定しておくことで、「どのような $\theta$ が起こりやすいか」という事前知識を表現することができます。

MAP 推定では、観測データ $x_i \ (i=1,\dots,N)$ が観測されたという**条件の下で**最も確率が大きくなる $\theta$ を求めます。すなわち、 $p(\theta | x_1, \dots, x_N)$ を最大とする $\theta$ を求めます。これは「 $x_i \ (i=1,\dots,N)$ が観測された」という**結果**のもとで「データの確率分布を表すモデルのパラメータが $\theta$ であったことが**原因**である」確率を意味するので、**事後確率**です。ベイズの定理から、事後確率は

$$
p(\theta | x_1,\dots,x_N) = \frac{p(x_1,\dots,x_N | \theta) p(\theta)}{p(x_1,\dots,x_N)}
$$

と書けました。右辺に着目してください。分子にある $p(x_1,\dots,x_N | \theta)$ を、確率モデル $f(x; \theta)$ を用いて $\prod_{i=1}^N f(x_i; \theta)$ と表すと、これは観測データ $x_i \ (i=1,\dots,N)$ の下での確率モデル $f(x; \theta)$ の**尤度**です。また、その右の $p(\theta)$ は、パラメータが $\theta$ という値になる**事前確率**でした。これが $g(\theta; \beta)$ という確率モデルで表されるとします。

今、事後確率を最大にするパラメータ $\theta$ を求めるために、右辺を最大化する $\theta$ を考えます。このとき、パラメータと無関係に決まる $p(x_1,\dots,x_N)$ を無視し

$$
p(x_1,\dots,x_N | \theta) p(\theta)
$$

だけに注目してこれを最大化するようにパラメータ $\theta$ を決定すると、その $\theta$ は求めたい左辺の事後確率を最大にする $\theta$ に一致します。上式は、尤度と事前分布の積になっています。このことから、**事後分布は尤度と事前分布の積に比例する**ということが分かります。

### 例：コイントスでのパラメータ推定

前節の最尤推定で用いたコイントスの例を再び考えます。
今度は MAP 推定を用いてパラメータを推定してみましょう。用いるデータは先程と同様の $10$ 回のコイントス結果です。
また、確率モデル $f(x; \theta)$ も同様に最尤推定の場合と同様のものを用います。
MAP 推定では確率モデル $f(x;\theta)$ の他に、事前分布 $p(\theta)$ を設計する必要があります。
$p(\theta)$ の決め方は推定を行う人がどのような事前知識（もしくは信念）を持っているかによって自由に選択することができますが、ここでは事前分布を

$$
p(\theta) = 12\theta (1-\theta)^2
$$

と設定します（[注釈7](#note7)）。 グラフを書くと $p(\theta)$ は $\theta = 1/3$ で最大値をとることがわかります。これは「コイントスが行われる前には、実験者は $\theta=1/3$ が最もあり得る可能性である」すなわち、「このコインは表よりも裏の方が出やすい」と信じている（事前知識を持っている）と解釈することができます。

事後分布は $\theta$ の関数として $p(x_1, \ldots, x_N | \theta) p(\theta)$ に比例するのでした。
これを具体的に計算すると

$$
p(x_1, \ldots, x_N | \theta) p(\theta) = \theta^5 (1-\theta)^5 \times 12 \theta (1-\theta)^2 = 12 \theta^6 (1-\theta)^7
$$

となります。これを $\theta$ の関数として最大化します。
最尤推定の時と同様に、この値そのものではなく、対数を取った値を最大とする $\theta$ を求めます。

$$
\log p(x_1, \ldots, x_N | \theta) p(\theta) = 6\log \theta + 7 \log (1-\theta) + const.
$$

ここで， $const.$ は $\theta$ に依存しない定数（具体的には $\log 12$ ）で、値を最大にする $\theta$ を決定するのに影響を及ぼしません。
これを $\theta$ で微分したものが $0$ に等しいという方程式を立てると、

$$
\begin{align}
\frac{6}{\theta} - \frac{7}{1-\theta} = 0 \\
\therefore \theta = \frac{6}{13}
\end{align}
$$

と解くことができます．これが MAP 推定で得られた推定値です。

最尤推定で得られた推定値は $\theta = 1/2$ だったので、MAP 推定は最尤推定よりも低めに $\theta$ を推定していることがわかります。
これは事前分布 $p(\theta)$ により $\theta$ は $1/2$ より小さい値が出やすいという事前知識を入れていたことを反映しています。逆に事前分布で大きい $\theta$ が出やすいという事前知識を入れれば、MAP 推定の値を最尤推定よりも大きくすることができます。

この例からも分かる通り、MAP 推定は事前分布の設定により得られる推定値が変化します。
この事は、MAP 推定の方が事前知識により柔軟なモデリングができる半面、誤った事前知識に基づいて事前分布を設計すると、推定値が実際のデータと乖離する可能性を持つことを意味します。

## 統計量

本節では、いくつかよく用いられる統計量を説明します。統計量とは、**観測されたデータの特徴を要約する数値**のことを指します。代表的な統計量として平均、分散、標準偏差を紹介します。

### 平均

**平均 (mean)** は、観測された数値を合計し、その数値の個数で割ったもののことを言います。たとえば、300 円、400 円、500 円の平均は、

$$
\frac{300 + 400 + 500}{3} = 400
$$

です。一般に、 $N$ 個のデータ $x_i \ (i=1,\dots,N)$ が観測されたとき、その平均は

$$
\begin{aligned}
\overline{x}
= \frac{x_1 + x_2 + \dots + x_N}{N}
= \frac{1}{N} \sum^{N}_{n=1} x_{n}
\end{aligned}
$$

と定義されます。
平均を表す記号として、 $\bar{x}$ や $\mu$ がよく用いられます。
データの分布において、平均はその重心に相当する値です。

### 分散

次に、**分散 (variance)** を紹介します。分散はよく $\sigma^2$ と表され、

$$
\sigma^2 = \frac{1}{N} \sum_{n=1}^N \left( x_n - \overline{x} \right)^2
$$

と定義されます。各データ点 $x_i \ (i=1,\dots,N)$ の値から、それらの平均 $\bar{x}$ を引き二乗した値（ $(x_i - \bar{x})^2 \ (i=1,\dots,N)$ ）の平均を計算しています。これは、「データが自らの平均より平均的にどのくらいばらついているか」を表します。平均が同じデータでも、全てのデータが平均付近にある場合は分散は小さく、平均よりも極めて大きなデータ点や小さなデータ点が多数ある場合、分散は大きくなります。分散にはもう一種類あります。以下のようなものです。

$$
\begin{aligned}
s^2 = \frac{1}{N - 1} \sum_{n=1}^N \left( x_n - \overline{x} \right)^2
\end{aligned}
$$

始めに登場した $\sigma^2$ の定義では $\frac{1}{N}$ となっていた部分が $\frac{1}{N - 1}$ に変わっています。
前者は**標本分散 (sample variance)** といい、後者は**不偏分散 (unbiased variance)** といいます。
これらの式の導出は他の文献に譲るとして、ここではその使い分けについて説明します。

例えば、全国の小学生の身長と体重の分散を調べたいとします。このとき、全国の小学生を一人の抜け漏れもなく調べたなら、集まったデータは**母集団 (population)** と呼ばれます。
一方、各都道府県の小学生を100人ずつ調べた場合、そのデータは**標本集団 (sample population)** と言います。
すなわち、母集団とは解析を行いたいデータ全ての集合を指し、標本集団とは母集団から抽出された一部のデータの集合を指します。一般に、標本集団のデータ数が少ないとき、標本分散は母集団の分散よりも小さくなることが知られています。不偏分散は、その差を補正することで、母集団の分散をより正確に推定するために用います。不偏分散は抽出するデータの数（$N$）を増やせば、いずれ母集団の分散に一致します。また、$N$ が大きいと標本分散と不偏分散はほぼ一致するので、たくさんのデータ数をとることができる状況では、両者に大きな違いが生じないことも多々あります。

分散を利用すると、データのばらつきを定量評価することができるようになります。例えば、同じ現象を複数回観測する実験を行った際、結果のばらつきが大きければ、その観測方法には問題がある可能性があります。もしくは、同じだと思っていた現象は、実は観測のたびに異なる現象であった可能性があります。このように、多数の試行の結果がある値に集まっていることが望ましいような状況において、ばらつきの度合いを定量し評価することは重要です。

### 標準偏差

次に**標準偏差 (standard deviation)** を紹介します。
分散はデータの平均からの差の**二乗**の平均でした。そのため単位は元の単位を二乗したものになります。例えばデータの単位が ${\rm kg}$ であれば、分散の単位は ${\rm kg}^2$ になります。そこで、分散 $\sigma^2$ の平方根 $\sigma$ を計算することで、データと単位が等しくなり、解釈が容易になります。この $\sigma$ を標準偏差と呼びます。

練習問題で具体的な計算手順の確認を行いましょう。以下の①と②のデータに対して、平均、分散、標準偏差を求めてください。ただし、今回は標本分散を使用することとします。

①の解答は以下の通りです。

$$
\begin{aligned}
\bar{x} &= \frac{1}{5} \left( -2 -1 + 0 + 1 + 2 \right) = 0 \\
\sigma^{2} &= \frac{1}{5} \left\{ \left( -2 -0 \right)^{2} + \left( -1 -0 \right)^{2} + (0 - 0)^{2} + (1 - 0)^{2} + (2 - 0)^{2} \right \} \\
&= \frac{1}{5} \times 10 = 2 \\
\sigma &= \sqrt{2}
\end{aligned}
$$

②の解答は以下の通りです。

$$
\begin{aligned}
\overline{x} &= \frac{1}{5} ( -4 -2 + 0 + 2 + 4 ) = 0 \\
\sigma^2 &= \frac{1}{5}
\left\{ (-4 -0)^2 + (-2 -0)^2 + (0 - 0)^2 + (2 - 0)^2 + (4 - 0)^2 \right\} \\
&= \frac{1}{5} \times 40 = 8 \\
\sigma &= \sqrt{8} = 2 \sqrt{2}
\end{aligned}
$$

これより、②のケースの方が分散が大きく、データのばらつきが大きいことがわかります。

### 相関係数

最後に**相関係数 (correlation coefficient)** を紹介します。

2 種類のデータが得られている状況では、両者の関係性の解析がしばしば重要となります。
相関係数は両者の関連の度合いを定量的に測るのに用います。

2 種類のデータ点がスカラー値で $N$ 個ずつある状況を考えます。それぞれを $x_n, y_n  \ (n=1,\dots,N)$ とした場合、相関係数の中でも良く用いられるピアソンの相関係数は以下のように定義されます。

$$
r = \frac{\sum_{n=1}^{N} (x_{n}-\bar{x})(y_{n}-\bar{y}) }{\sqrt{\left( \displaystyle \sum_{n=1}^N(x_{n}-\bar{x})^2 \right) \left( \displaystyle \sum_{n=1}^N(y_{n}-\bar{y})^2 \right) }}
$$

上式のように相関係数を表す記号は $r$ を用いるのが一般的です。
相関係数 $r$ は常に $-1 \leq r \leq 1$ になり、相関が認められるとき、 $r$の値が正の場合は**正の相関**があるといい、逆に負の値のときは**負の相関**があるといいます。

2 種類のデータ間の相関が強いほど $r$ の絶対値は大きくなります。
しかし、「$r$ がいくつ以上ならば相関があると思って問題ない」という閾値はタスクごとに異なり、例えば $r=0.2$ から相関ありと判断してよいかは一概には言えません。本資料では説明を省略しますが、無相関検定などの手法を利用することで客観的な判断を行うことができます。

<hr />

<div class="alert alert-info">
**注釈 1**

ここでは概念の説明を簡単にするため、この例のように離散的な値をとる確率変数を考え、特に明示しない限り連続値の確率変数は考えないことにします。

[▲上へ戻る](#ref_note1)
</div>

<div class="alert alert-info">
**注釈 2**

$x$ は $1, 2, 3, 4, 5, 6$ のいずれか。すなわち $x \in \{1, 2, 3, 4, 5, 6\}$ です。

[▲上へ戻る](#ref_note2)
</div>

<div class="alert alert-info">
**注釈 3**

$y$ は 2 つ目のコインがとりうる状態で、この場合、「表」と「裏」という値のいずれか。

[▲上へ戻る](#ref_note3)
</div>

<div class="alert alert-info">
**注釈 4**

$x$ は 1 つ目のコインがとりうる状態で、この場合、「表」と「裏」という値のいずれか。

[▲上へ戻る](#ref_note4)
</div>

<div class="alert alert-info">
**注釈 5**

以降、このことを「データの分布を推定する」と言うことがあります。また、観測されたデータのみから各データの発生確率（頻度とも捉えられる）を求めたものは**経験分布（empirical distribution）**とも呼ばれ、本節で説明しているのは正確にはこの経験分布を確率モデルで近似する方法です。

[▲上へ戻る](#ref_note5)
</div>

<div class="alert alert-info">
**注釈 6**

この関数には、ベルヌーイ分布という名前がついています。

[▲上へ戻る](#ref_note6)
</div>

<div class="alert alert-info">
**注釈 7**

先頭の $12$ は $p(\theta)$ $0$ から $1$ まで積分した値が $1$ となるように決めています。これにより、$p(\theta)$ が $0 \leq \theta \leq 1$ の上の確率分布となります。

[▲上へ戻る](#ref_note7)
</div>