Soluciones de la Práctica de la Unidad 3

Instrucciones generales para resolver los problemas de esta práctica:

  1. Abrir RStudio y crear un nuevo proyecto llamado unidad3, para guardar allí todos los archivos que usaremos. Asegurarse de que RStudio esté trabajando con este proyecto abierto.
  2. Al comenzar a resolver cada ejercicio:
  1. Eliminar todos los objetos del Global Environment, para evitar confusiones con objetos que hayan sido creados para resolver otro problema.
  2. Crear y guardar en la carpeta del proyecto un nuevo script con el nombre ejercicio_*.R para almacenar de manera organizada la solución de cada problema (por ejemplo, ejercicio_01.R, ejercicio_02.R, etc.)
  3. A menos que se indique lo contrario, utilizar cada uno de estos scripts para escribir el código que crea la función pedida en el ejercicio y también el código con ejemplos para usarla.

Ejercicio 1

  1. Escribimos la función y la usamos:

    # Función f1
    f1 <- function(x1, x2, x3) {
      resultado <- x1 / x2 + x3^2 + x2 * x3
      return(resultado)
    }
    
    # Ejemplo de su uso
    f1(5, 2, 3)
    [1] 17.5
  2. Escribimos la función y la usamos:

    # Función f2
    f2 <- function(x1, x2 = 1, x3 = 1) {
      resultado <- x1 / x2 + x3^2 + x2 * x3
      return(resultado)
    }
    
    # Ejemplos de su uso
    f2(5, 2, 3)
    [1] 17.5
    f2(5)
    [1] 7
    f2(5, 2)
    [1] 5.5
    f2(5, x3 = 3)
    [1] 17
    f2(x2 = 2, x3 = 3)
    Error in f2(x2 = 2, x3 = 3): argument "x1" is missing, with no default
  3. Escribimos la función y la usamos:

    # Función f3
    f3 <- function(x1, x2 = 1, x3 = 1) {
      if (x1 < 0 || x2 < 0 || x3 < 0) {
        return(-100)
      } else {
        resultado <- x1 / x2 + x3^2 + x2 * x3
        return(resultado)
      }
    }
    
    # Ejemplos de su uso
    f3(5, 2, 3)
    [1] 17.5
    f3(-5, 2, 3)
    [1] -100
    f3(-5)
    [1] -100
    f3(5, x3 = -3)
    [1] -100

Ejercicio 2

Sin importar el signo de a y b ni cuál es mayor o menor, la secuencia de todos los enteros se puede generar fácilmente con la expresión a:b que usamos como parte de la estructura for:

1:4
[1] 1 2 3 4
4:1
[1] 4 3 2 1
3:3
[1] 3
-2:4
[1] -2 -1  0  1  2  3  4
-2:-8
[1] -2 -3 -4 -5 -6 -7 -8

Por lo tanto, si iteramos con un for a través de la secuencia a:b, la función puede definirse así:

#' Suma de una secuencia de números enteros
#' 
#' @description
#' Calcula la suma de un rango de números enteros, incluyendo los extremos. 
#' 
#' @param a,b números enteros
#'
#' @return suma de la secuencia
#'
#' @examples
#' suma_secuencia(1, 3)
#' suma_secuencia(-2, 3)
#' suma_secuencia(-3, -5)
#' suma_secuencia(3, 3)
#' 
suma_secuencia <- function(a, b) {
  suma <- 0
  for (i in a:b) {
    suma <- suma + i
  }
  return(suma)
}

Ejemplos de su uso:

suma_secuencia(1, 3)
[1] 6
suma_secuencia(30, 40)
[1] 385
suma_secuencia(5, 2)
[1] 14
suma_secuencia(-2, 3)
[1] 3
suma_secuencia(-7, -5)
[1] -18
suma_secuencia(-3, -5)
[1] -12
suma_secuencia(-3, -3)
[1] -3
suma_secuencia(3, 3)
[1] 3

Ejercicio 3

#' Clasificación de un triángulo
#' 
#' @description
#' Clasifica a un triángulo según las longitudes de sus lados, en escaleno, isósceles 
#' o equilátero. 
#'
#' @details 
#' Se evalúa la desigualdad triangular. Si las medidas de los lados no corresponden 
#' a un triángulo, la función devuelve "no es un triángulo". 
#' 
#' @param a,b,c números reales positivos
#'
#' @return valor carácter que indica el tipo de triángulo.
#'
#' @examples
#' triangulos(2, 3, 4)
#' triangulos(2, 3, 10)
#' 
triangulos <- function(a, b, c) {
    if (a > b + c || b > a + c || c > a + b) {
        return("no es triángulo")
    } else if (a == b & a == c) {
        return("equilátero")
    } else if (a == b || a == c || b == c) {
        return("isósceles")
    } else {
        return("escaleno")
    }
}

Alternativamente, podemos prescindir de las estructuras anidadas, gracias a la existencia de las sentencias return en cada camino posible. Si alguna condición es TRUE, enseguida se devuelve el valor que corresponda y se detiene la ejecución de la función. El resto no se evalúa, lo cual hace innecesario usar else if o estructuras anidadas:

triangulos <- function(a, b, c) {
    if (a > b + c || b > a + c || c > a + b) {
      return("no es triángulo")
    }
    if (a == b & a == c) {
      return("equilátero")
    }
    if (a == b || a == c || b == c) {
      return("isósceles")
    }
    return("escaleno")
}

Ejemplos de uso:

triangulos(2, 3, 4)
[1] "escaleno"
triangulos(2, 3, 10)
[1] "no es triángulo"

Ejercicio 4

#' Punto dentro de la elipse
#' 
#' @description
#' Determina si un punto está contenido dentro de la elipse definida por la fórmula 
#' (x - 6)^2 / 36 + (y + 4)^2 / 16 = 1
#'
#' @details 
#' Por defecto se evalúa al origen de coordenadas.
#' 
#' @param x,y coordenadas, números reales. Por defecto valen 0. 
#'
#' @return valor lógico indicando si el punto está dentro de la elipse descripta.
#'
#' @examples
#' elipse(3, 7)
#' elipse(6, -4)
#' elipse()
#' 
elipse <- function(x = 0, y = 0) {
  valor <- (x - 6)^2 / 36 + (y + 4)^2 / 16
    return(valor <= 1)
}

Ejemplos de uso:

elipse(3, 7)
[1] FALSE
elipse(6, -4)
[1] TRUE
elipse()
[1] FALSE

Ejercicio 5

Si bien hay muchas formas de resolver este ejercicio, tal vez usando estructuras iterativas y condicionales, conviene pensarlo desde un punto de vista matemático. La suma de los números impares de la fila \(n\) resulta igual a \(n^3\). Para darse cuenta, conviene pensar en los siguientes puntos:

  • La suma de los primeros \(n\) números naturales es: \(n(n+1)/2\). Por ejemplo, si \(n=3\), \(1+2+3=6=3*4/2\).
  • La suma de los primeros \(n\) números naturales impares es: \(n^2\). Por ejemplo, si \(n=3\), \(1+3+5=9=3^2\).
  • En la fila \(i\), hay exactamente \(i\) números impares. Por ejemplo, en la fila \(i=3\), hay 3 números: 7, 9 y 11.
  • Desde la fila 1 hasta la fila \(n\) inclusive, hay \(n(n+1)/2\) números impares. En la fila 1 hay 1, en la fila 2 hay 2, etc. Entonces hasta la fila \(n\) hay \(1+2+3+...+n = n(n+1)/2\) (suma de los primeros \(n\) naturales).
  • La suma de los números impares desde la fila 1 hasta la fila \(n\) inclusive entonces es igual a la suma de los primeros \(n(n+1)/2\) números impares: \(\Big(\frac{n(n+1)}{2}\Big)^2\).
  • Del mismo modo, la suma de los números impares desde la fila 1 hasta la fila \(n-1\) inclusive es igual a la suma de los primeros \((n-1)n/2\) números impares: \(\Big(\frac{(n-1)n}{2}\Big)^2\).
  • Como sólo queremos la suma de los impares que están en la fila \(n\), podemos calcular la suma desde la fila 1 hasta la \(n\) inclusive, y restarle la suma desde la fila 1 hasta la fila \(n-1\) inclusive. El número que buscamos entonces es:

\[ \Big(\frac{n(n+1)}{2}\Big)^2 - \Big(\frac{(n-1)n}{2}\Big)^2 = n^3 \]

El código de la función puede ser sencilamente:

#' Suma de fila de la pirámide
#' 
#' @description
#' La pirámide se arma con números impares, empezando con el 1 en la cima, el 3 y 5 
#' en la segunda fila, y así sucesivamente. Devuelve la suma de los números ubicados 
#' en la fila ingresada.
#'
#' @details 
#' Se puede demostrar que la suma de los números en la n-ésima fila es igual a 
#' n al cubo.
#' 
#' @param n Número natural
#'
#' @return suma de los números ubicados en la n-ésima fila de la pirámide.
#'
#' @examples
#' suma_piramide(1)
#' suma_piramide(2)
#' suma_piramide(3)
#' 
suma_piramide <- function(n) {
  return(n^3)
}

Ejemplos:

suma_piramide(1)
[1] 1
suma_piramide(2)
[1] 8
suma_piramide(3)
[1] 27
# Evaluamos la suma de cada una de las primeras 10 filas
for (n in 1:10) {
  suma <- suma_piramide(n)
  cat("Los impares de la fila", n, "suman", suma, "\n")
}
Los impares de la fila 1 suman 1 
Los impares de la fila 2 suman 8 
Los impares de la fila 3 suman 27 
Los impares de la fila 4 suman 64 
Los impares de la fila 5 suman 125 
Los impares de la fila 6 suman 216 
Los impares de la fila 7 suman 343 
Los impares de la fila 8 suman 512 
Los impares de la fila 9 suman 729 
Los impares de la fila 10 suman 1000 

Ejercicio 6

  1. Paso a paso:

    1. En el ambiente global primero se definen las funciones f y g y las variables globales a y b, con los valores 6 y 1, respectivamente.
    2. Se invoca g(a, b), resultando en que en el ambiente local de g, x recibe el valor 6 e y el valor 1.
    3. En el ambiente local de g se crea la variable local b, con valor 6 - 2 * 1 = 4.
    4. Desde el ambiente local de g se invoca f(b), donde b vale 4.
    5. En el ambiente local de f, a recibe el valor 4, el cual es actualizado por (4-10)*(4+10)=-84 para finalmente devolver -84.
    6. De regreso en el ambiente local de g, la variable c recibe el valor b*f(b)=4*f(4)=4*(-84)=-336 y se devuelve -336.
    7. Para definir la variable d se vuelve a invocar f esta vez sin argumentos explícitos, por lo que en el ambiente local de f, a recibe el valor 10 y f() = 0. El valor final de d es f() - c = 0 - (-336) = 336.
    8. En el ambiente global entonces el resultado de g(a, b) es 336.
  2. En este ejemplo, el identificador a representa dos variables distintas: una de ellas definida en el Global Environment y la otra en el ámbito local de la función f1. Lo mismo ocurre con x: representa a una variable en el ambiente global, a otra en el ambiente de la función f1 y a una tercera en el ambiente de la función f2. Al ejecutar el algoritmo se obtendrían los siguientes resultados:

    1. En el ambiente global, se definen las funciones f1 y f2 y las variables x e y con valores: x = 3 e y = 5.
    2. Desde el ambiente global, se invoca a la función f1, donde el parámetro formal a recibe el valor del parámetro real x (a = 3), mientras que el parámetro formal b recibe el valor del parámetro real y (b = 5). Dentro de la función f1:
      1. Se crea una nueva variable x que recibe el valor x = a + b = 8. Esta x no tiene nada que ver con la del ambiente global.
      2. Se crea una nueva variable y que recibe el valor y = x + 2 = 8 + 2 = 10. Esta y no tiene nada que ver con la del ambiente global.
      3. La función devuelve el valor y = 10.
    3. En el ambiente global, se le asigna a la variable a el valor recién devuelto: a = 10. Esta a no tiene nada que ver con la variable local de la función f1.
    4. Desde el ambiente global, se llama a la función f2, donde el parámetro formal x recibe el valor del parámetro real a (x = 10). Esta x no tiene nada que ver con las anteriores. Dentro de la función f2:
      1. Se calcula el cuadrado de x: \(10^2\) = 100
      2. Se devuelve el valor 100.
    5. En el ambiente global, se suma x + f2(a) = 3 + 100 = 103 y se asigna este valor a z.
    6. El algoritmo escribe el valor de z, 103.
    7. El algoritmo intenta escribir a + b = 10 + ?, pero produce error, puesto que b no está definida en el ambiente global, no tiene asignado ningún valor. La única variable b que existe está en el ámbito de la función f1, no en el Global Environment.
  3. Primero, en el ambiente global se define la función f con tres argumentos: x (sin valor por defecto), y y z (opcionales). La función devuelve una combinación lineal de estas tres cantidades. En segundo lugar, se invoca sucesivamente la función f para definir cuatro variables globales en el siguiente orden:

    1. a = f(10) = f(10, 5, 10 + 5) = (10+5) - 10 - 5 = 0
    2. b = f(10, 10) = f(10, 10, 10 + 10) = (10+10) - 10 - 10 = 0
    3. c = f(10, 10, 10) = 10 - 10 - 10 = -10
    4. d = f(10, z = 10) = f(10, 5, 10) = 10 - 10 - 5 = -5

    Finalmente, se imprime el resultado de sumar las cuatro cantidades: 0 + 0 - 10 - 5 = -15

Ejercicio 7

Hemos definido a las funciones como subalgoritmos que devuelven un objeto. En este caso no nos interesa devolver nada, sino sólo escribir mensajes en la consola, por eso podemos omitir el uso de return():

#' Resolviendo ecuaciones de segundo grado
#' 
#' @description
#' Encuentra las soluciones de una ecuación de segundo grado a partir del uso de
#' la formula resolvente. Se deben ingresar los coeficientes del polinomio de 
#' segundo grado asociado. 
#'
#' @details 
#' La función imprime un mensaje con el detalle de las soluciones, incluyendo si 
#' esta ecuación tiene dos soluciones distintas, una solución doble o ninguna 
#' solución dentro de los números reales. La función devuelve un mensaje de error 
#' si el coeficiente cuadrático es 0. 
#' 
#' @param a,b,c Números reales
#'
#' @return NULL, junto con un mensaje con el detalle de las soluciones.
#'
#' @examples
#' resolvente(1, -1, -2)
#' resolvente(1, 2, 1)
#' resolvente(1, 1, 1)
#' resolvente(0, 1, 1)
#' 
resolvente <- function(a, b, c) {
  if (a == 0) {
    stop("(a) debe ser distinto de cero")
  }
  discriminante <- b^2 - 4 * a * c
  if (discriminante > 0) {
    x1 <- (-b - sqrt(discriminante)) / (2 * a)
    x2 <- (-b + sqrt(discriminante)) / (2 * a)
    cat("Hay dos soluciones reales", x1, "y", x2, "\n")
  } else if (discriminante == 0) {
    x1 <- -b / (2 * a)
    cat("Hay una solución real doble:", x1, "\n")
  } else {
    cat("Las soluciones son complejas.\n")
  }
}

Ejemplo de uso:

resolvente(1, -1, -2)
Hay dos soluciones reales -1 y 2 
resolvente(1, 2, 1)
Hay una solución real doble: -1 
resolvente(1, 1, 1)
Las soluciones son complejas.
resolvente(0, 1, 1)
Error in resolvente(0, 1, 1): (a) debe ser distinto de cero

Comentarios adicionales. Como hemos mencionado, cuando no se incluye un return(), igualmente la función devuelve algo, que es el resultado de la última expresión ejecutada. En todas las situaciones, en esta función la última expresión ejecutada es un cat(). Además de escribir un texto, esta función devuelve un valor NULL invisible (no se imprime en la consola, pero está). Podemos corrobar este comportamiento si usamos la función de esta forma:

# Imprime texto en la consola y devuelve un NULL, que se guarda enresultado
resultado <- resolvente(1, -1, -2)
Hay dos soluciones reales -1 y 2 
# Imprimimos resultado y encontramos que tiene guardado un NULL
resultado
NULL

Sería interesante que esta función pueda devolver las soluciones. Hasta acá sabemos que las funciones pueden devolver un único objeto. Esto puede ser un inconveniente para este problema, ya que nos interesa devolver dos valores (dos soluciones reales), un valor (una solución real doble) o ningún valor (ninguna solución real). Más adelante veremos cómo hacer para poder devolver distinta cantidad de objetos, agrupándolos en otra estructura de datos.

Ejercicio 8

La solución propone chequear primeramente que el número de entrada sea entero y positivo. Si detecta que una de estos requisitos no se cumple, emite un warning y devuelve FALSE.

En caso de que se cumplan estos requisitos, el programa directamente devuelve TRUE si el número analizado es 2 o 3, ya que sabemos que estos primeros naturales son primos. Para el resto de los números, divide a n por todos los naturales desde el 2 hasta n - 1. Si al hacer esta división, encuentra un resto igual a cero, significa que n es compuesto. Si ninguna división produce resto cero, entonces n es primo.

Por ejemplo, para n = 7, se hace:

  • 7 %% 2 = 1, sigue.
  • 7 %% 3 = 1, sigue.
  • 7 %% 4 = 3, sigue.
  • 7 %% 5 = 2, sigue.
  • 7 %% 6 = 1, termina la iteración.
  • Dado que no se encontraron divisores para 7, es un número primo, se devuelve TRUE

Para n = 9, se hace:

  • 9 %% 2 = 1, sigue.
  • 9 %% 3 = 0, devuelve FALSO.
  • Dado que se encontró que 9 es múltiplo de 3, no es un número primo y se devolvió FALSE.
#' Evaluación de números primos
#' 
#' @description
#' Determina si un número entero es primo o no. 
#'
#' @details 
#' La función devuelve un mensaje de advertencia si no se ingresa un número natural 
#' mayor que 1. El resultado en este caso será FALSO.
#' 
#' @param n Número natural
#'
#' @return Un valor lógico, TRUE si el número es primo, FALSE si no lo es.
#'
#' @examples
#' es_primo(47)
#' es_primo(253)
#' es_primo(2)
#' 
es_primo <- function(n) {
  if (n %% 1 != 0) {
    warning("(n) no es entero")
    return(FALSE)
  }
  
  if (n <= 1) {
    warning("(n) no es mayor a 1")
    return(FALSE)
  }
  
    if (n > 3) {
        for (i in 2:(n - 1)) {
            if (n %% i == 0) {
                return(FALSE)
            }
        }
    }
  
  # solo se llega acá si no se devolvió FALSE antes, es decir, si n es primo
    return(TRUE)
}

Ejemplos de uso:

es_primo(47)
[1] TRUE
es_primo(253)
[1] FALSE
es_primo(2)
[1] TRUE
es_primo(7.18)
Warning in es_primo(7.18): (n) no es entero
[1] FALSE
es_primo(0)
Warning in es_primo(0): (n) no es mayor a 1
[1] FALSE

Observación: se puede plantear un algoritmo más eficiente, que realice menos iteraciones. No es necesario iterar hasta n - 1, si no hasta el entero inmediato menor a \(\sqrt{n}\). Si no se encontró que n sea múltiplo de ningún valor menor a \(\sqrt{n}\), tampoco lo será con los que siguen. Por otro lado, sería suficiente hacer las divisiones con respecto a los números primos menores \(\sqrt{n}\). Se deja propuesto elaborar esta solución alternativa.

Ejercicio 9

Para poder resolver una división usando solamente sumas y restas, tenemos que pensar que, por ejemplo, hacer 14 dividido 3 nos da cociente 4 y resto 2, porque el 3 “entra” 4 veces en el 14 y todavía sobran 2. Es decir, a 14 le podemos restar el 3 cuatro veces hasta que ya no se lo podamos restar más, quedando un resto de 2. Entonces la idea es empezar diciendo que el resto es el dividendo (al principio, nos resta todo el dividendo) y restarle iterativamente el valor del divisor hasta que el resto se haga menor que el divisor. Mientras tanto, tenemos que ir contando cuántas restas se hacen, puesto que eso será el valor del cociente. Ejemplo:

  • dividendo = 14, divisor = 3, resto = 14.
  • 14 - 3 = 11, digo que el resto es 11 y cuento que ya hice una resta con cociente = 1.
  • 11 - 3 = 8, digo que el resto es 8 y cuento que ya hice dos restas con cociente = 2.
  • 8 - 3 = 5, digo que el resto es 5 y cuento que ya hice tres restas con cociente = 3.
  • 5 - 3 = 2, digo que el resto es 2, cuento que ya hice cuatro restas con cociente = 4.
  • Como ya obtuve un resto menor que el divisor, me detengo, con este resultado: cociente = 4 y resto = 2.

Como a priori no sabemos cuántas iteraciones de este proceso tenemos que hacer, empleamos un while.

#' Cociente de la división de números naturales
#' 
#' @description
#' Obtiene el cociente entero y el resto en la división de dos números naturales
#'
#' @details 
#' La función imprime un mensaje con todos los detalles de la división, con el 
#' dividendo, divisor, cociente y resto. Sin embargo, la función devuelve solo el 
#' cociente entero. 
#' 
#' @param dividendo,divisor Números naturales.
#'
#' @return El cociente entero de la división de ambos números.
#'
#' @examples
#' cociente(1253, 4)
#' cociente(3, 4)
#' 
cociente <- function(dividendo, divisor) {
    resto <- dividendo
    cociente <- 0
    while (resto >= divisor) {
        cociente <- cociente + 1
        resto <- resto - divisor
    }
    cat("Dividendo:", dividendo, "\n")
    cat("Divisor:", divisor, "\n")
    cat("Cociente:", cociente, "\n")
    cat("Resto:", resto, "\n")
    return(cociente)
}

Ejemplos de uso:

cociente(1253, 4)
Dividendo: 1253 
Divisor: 4 
Cociente: 313 
Resto: 1 
[1] 313
cociente(3, 4)
Dividendo: 3 
Divisor: 4 
Cociente: 0 
Resto: 3 
[1] 0

Ejercicio 10

#' Cálculo del máximo común divisor
#' 
#' @description
#' Calcula el máximo común divisor entre dos números naturales
#'
#' @details 
#' Aplica el algoritmo de Euclides para encontrar el MCD. Dividir al mayor por el 
#' menor y registrar el resto. Si el resto es cero, el divisor es el MCD. Si el 
#' resto no es cero, hay que dividir el divisor por el resto y hacer la misma 
#' evaluación. Es decir, el divisor pasa a ser el nuevo dividendo y el resto, el 
#' nuevo divisor. Así hasta obtener el resto cero y tener el MCD. 
#' 
#' @param a,b Números naturales.
#'
#' @return El máximo común divisor de ambos números.
#'
#' @examples
#' max_com_div(100, 24)
#' max_com_div(25, 100)
#' max_com_div(24, 24)
#' 
max_com_div <- function(a, b) {
  
    # Establecer como dividendo al mayor y como divisor al menor
    if (a > b) {
        dividendo <- a
        divisor <- b
    } else {
        dividendo <- b
        divisor <- a
    }
  
    # Iniciar al resto como igual al dividendo
    resto <- dividendo
    
    # Aplicar algortimo de Euclides
    while (resto != 0) {
      
      # Dividir al mayor por el menor y registrar el resto
        resto <- dividendo %% divisor
        
        # Si el resto es cero (lo chequearemos en la condición del while), el 
        # divisor es el mcd, por las dudas nos preparamos para entregarlo.
        mcd <- divisor
        
        # Si el resto no es cero (lo chequearemos en la condición del while), hay
        # que dividir el divisor por el resto y hacer la misma evaluación. Es decir,
        # el divisor pasa a ser el nuevo dividendo y el resto, el nuevo divisor
        dividendo <- divisor
        divisor <- resto
    }
    
    # Devolver mcd
    return(mcd)
}

Ejemplos de uso:

max_com_div(100, 24)
[1] 4
max_com_div(25, 100)
[1] 25
max_com_div(24, 24)
[1] 24

Otra forma:

max_com_div <- function(a, b) {
    # Establecer como dividendo al mayor y como divisor al menor
    if (a > b) {
        dividendo <- a
        divisor <- b
    } else {
        dividendo <- b
        divisor <- a
    }
    # Iniciar al resto como igual al dividendo
    resto <- dividendo
    # Aplicar algortimo de Euclides
    while (resto > 0) {
        resto <- dividendo %% divisor
        if (resto == 0) return(divisor)
        dividendo <- divisor
        divisor <- resto
    }
}

Ejercicio 11

  1. En el script funciones_unidad3.R se implementa la función combinatorio(m,n) y se guarda junto con fact(). Su contenido debe ser:

    # ---------------------------------------------------------------
    # DEFINICIÓN DE FUNCIONES (funciones.R)
    # ---------------------------------------------------------------
    
    #' Cálculo de factoriales
    #' 
    #' @description
    #' Calcula el factorial de números enteros no negativos.
    #'
    #' @details 
    #' Produce un error si se quiere calcular el factorial de un número negativo.
    #' 
    #' @param n Número entero no negativo para el cual se calcula el factorial.
    #'
    #' @return El factorial de n.
    #'
    #' @examples
    #' fact(5)
    #' fact(0)
    #' 
    fact <- function(n) {
      if (n < 0 || n != floor(n)) {
        stop("n debe ser entero no negativo.")
      }
      resultado <- 1
      if (n > 0) {
          for (i in 1:n) {
              resultado <- resultado * i
          }
      }
      return(resultado)
    }
    
    #' Cálculo de números combinatorios
    #' 
    #' @description
    #' Calcula números combinatorios invocando a la función fact()
    #'
    #' @param m,n números naturales
    #'
    #' @return El número combinatorio m en n
    #'
    #' @examples
    #' combinatorio(3, 1)
    #' 
    combinatorio <- function(m, n) {
        return(fact(m) / (fact(m - n) * fact(n)))
    }
  2. El contenido del script ejercicio11.R puede ser:

    # ---------------------------------------------------------------
    # PROGRAMA: "Ejemplificar propiedades de los nros combinatorios"
    # ---------------------------------------------------------------
    
    # Cargar funciones
    source("funciones_unidad3.R")
    cat("Propiedad 1: C(m, 0) = 1. Ejemplo:\n")
    res1 <- combinatorio(5, 0)
    cat("C(5, 0) =", res1, "\n\n")
    
    cat("Propiedad 2: C(m, m) = 1. Ejemplo:\n")
    res1 <- combinatorio(5, 5)
    cat("C(5, 5) =", res1, "\n\n")
    
    cat("Propiedad 3: C(m, 1) = m. Ejemplo:\n")
    res1 <- combinatorio(5, 1)
    cat("C(5, 1) =", res1, "\n\n")
    
    cat("Propiedad 4: C(m, n) = C(m, m-n). Ejemplo:\n")
    res1 <- combinatorio(5, 2)
    res2 <- combinatorio(5, 3)
    cat("C(5, 2) =", res1, "\n")
    cat("C(5, 3) =", res2, "\n\n")
    
    cat("Propiedad 5: C(m, n) = C(m-1, n-1) + C(m-1, n). Ejemplo:\n")
    res1 <- combinatorio(5, 2)
    res2 <- combinatorio(4, 1) + combinatorio(4, 2)
    cat("C(5, 2) =", res1, "\n")
    cat("C(4, 1) + C(4, 2) =", res2)
    Propiedad 1: C(m, 0) = 1. Ejemplo:
    C(5, 0) = 1 
    
    Propiedad 2: C(m, m) = 1. Ejemplo:
    C(5, 5) = 1 
    
    Propiedad 3: C(m, 1) = m. Ejemplo:
    C(5, 1) = 5 
    
    Propiedad 4: C(m, n) = C(m, m-n). Ejemplo:
    C(5, 2) = 10 
    C(5, 3) = 10 
    
    Propiedad 5: C(m, n) = C(m-1, n-1) + C(m-1, n). Ejemplo:
    C(5, 2) = 10 
    C(4, 1) + C(4, 2) = 10
  3. A continuación, se prueba pasando un valor de n mayor que m:

    combinatorio(4, 5)
    Error in fact(m - n): n debe ser entero no negativo.

    Como se observa, la función falla porque internamente se invoca a la función fact() con un número negativo, y la misma fue programada para disparar un error en ese caso. El mensaje n debe ser entero no negativo no es demasiado claro, dado que es emitido por la función fact() y se refiere a su argumento n, no al n de combinatorio(). Podríamos mejorar esta situación de varias formas. Una de ellas podría ser disparar un error antes, dentro de la función combinatorio(), para evitar cualquier procesamiento si \(n>m\):

    combinatorio <- function(m, n) {
      if (n > m) {
        stop("m debe ser menor o igual que n ")
      }
        return(fact(m) / (fact(m - n) * fact(n)))
    }
  4. Se generaliza la función combinatorio(m, n) para calcular números combinatorios con y sin reposición. Para esto se incluye el argumento r, que toma el valor lógico TRUE si el cálculo es con reposición o FALSE en caso contrario.

    Agregamos lo siguiente al archivo unidad3_funciones.R. Notar que esta vez no usamos return(), pero igualmetne se devuelve el resultado deseado por ser lo último que se evalúa:

    #' Cálculo de números combinatorios 
    #' 
    #' @description
    #' Calcula el número combinatorio m en n, con o sin reposición según r
    #'
    #' @param m,n números naturales
    #' @param r valor lógico, sobre si el cálculo es con o sin repetición, F por defecto
    #'
    #' @return El número combinatorio m en n, con o sin reposición
    #'
    #' @examples
    #' combinatorio2(10, 2, TRUE)
    #' combinatorio2(10, 2)
    #' 
    combinatorio2 <- function(m, n, r = FALSE) {
      if (r) {
        combinatorio(m + n - 1, n)
      } else {
        combinatorio(m, n)
      }
    }

    En el script ejercicio_11.R probamos la nueva función:

    # Números combinatorios con y sin reposición
    
    m <- 5
    n <- 2
    cat(m, "tomados de a", n, "sin resposición:", combinatorio2(m, n), "\n")
    cat(m, "tomados de a", n, "con resposición:", combinatorio2(m, n, TRUE))
    5 tomados de a 2 sin resposición: 10 
    5 tomados de a 2 con resposición: 15

    La cantidad de combinaciones con reposición siempre es mayor, salvo para n = 1 donde ambas coinciden.

Ejercicio 12

La documentación se ha incluido en cada una de las respuestas anteriores.