i elevado a i

Una serie de artículos acerca de la exponenciación con números complejos, que di por finalizada en su tercera entrega, y a los que el lector puede recurrir si no entiende algo de lo que en el presente artículo se explica, voy a ampliarla para el curioso caso de ii, y digo curioso pues el resultado es un número real.

Vimos que la generalización de la exponenciación, ab, sean a y b reales1 o complejos, es:

Exponenciación generalizada

En el presente caso, a = i, y si bien el logaritmo complejo existe, aplicando la fórmula de Euler, podremos obtener la forma exponencial para calcular fácilmente ii. En primer lugar, vamos a expresar i en forma polar para obtener dos datos que necesitaremos: el módulo y el argumento. El módulo, obviamente, será 1 y el argumento será π/2 radianes, pues i forma un ángulo recto con la parte real o eje de las ordenadas:

El número imaginario en el plano cartesiano complejo

Por lo tanto, i = 1π/2 en polar, mientras que en binómica es (0 +1i). De esta última, gracias a que conocemos tanto el módulo como el argumento, podemos pasar a la trigonométrica:

Expresión que se deduce por trigonometría:

forma trigonometrica del número complejo

Gracias a la fórmula de Euler donde x es el argumento:

Formula de EulerPodemos convertir, finalmente, la unidad imaginaria a la forma exponencial:

Por lo que:

Como i = √-1:

Y ya hemos calculado ii.

En rojo el número i y en azul r = ii en el plano complejo.


Si a ∈ ℝ, a > 0

Ecuación de la asíntota oblicua

Es frecuente que a los alumnos se les enseñe que, si una función tiene un asíntota oblicua que viene dada por y = ax + b, los términos a y b se calcularán a partir de dos fórmulas que deben memorizar:

término b

(1)

término a

(2)

Pero, ¿de dónde provienen? Si tenemos poca memoria y/o preferimos entender el porqué de las cosas, algo bastante conveniente en matemáticas, debemos tener en cuenta que, para una x infinitamente grande, por las propiedades de los límites, se cumplirá f(x) – y = 0:

ecuación asintota oblicua

(3)

Estas propiedades son debidas a que los límites cumplen con las condiciones de linealidad, vamos a aplicarlas paso a paso para ver cómo se obtiene el término b:

obtencion termino bAhora sólo resta aislarlo y obtendremos la fórmula inicial (1). En la ecuación (3), y dado que b no depende de x, podemos aislar a y obtendremos la fórmula (2).

 

Funciones

En azul la función f(x) = x²/(x+1) En rojo su asíntota oblicua y = x – 1

Cambiando un poco de tema, pero no demasiado, puede verse que si a = 0, entonces se trata de una asíntota horizontal, por lo que podemos ver las asíntotas horizontales como un caso particular de las oblicuas.

La Inteligencia Artificial como último legado del ser humano

En este artículo divago, sin pretensión, sobre diferentes temas que me han interesado a lo largo de la vida: la finitud de la misma (tanto la individual como la del conjunto de los seres de este planeta), las distancias insalvables del espacio y su hostilidad a la vida, el sentido de esta… y la informática. Desde luego, el ser humano es algo más que inteligencia, así como para la vida la inteligencia es sólo una herramienta más, como las garras y los dientes o la capacidad de ver de noche, pero a largo plazo, tal vez sea lo único que podamos hacer perdurar.

El planeta tierra no podrá sustentar indefinidamente la vida, especialmente las formas de vida más complejas, como la nuestra, que son también las más delicadas: tenemos mucha menos resistencia que, por ejemplo, las cucarachas, y estás a su vez se quedan cortas frente a determinadas bacterias y virus, algunos capaces de permanecer latentes durante siglos antes de volver a despertar. Ahora mismo, nuestra civilización se enfrenta a dos grandes problemas:

Incluso si superamos estos problemas, obviamente llegarán otros en el futuro que ahora mismo no nos podemos ni imaginar, no tienen porqué ser un virus o un meteorito. Si, finalmente superamos los retos actuales y los futuros, la expansión del sol en su evolución a gigante roja, arrasará el planeta Tierra: su ecuador coincidirá con la órbita actual de Marte.

En ninguno de los otros planetas del sistema solar la vida es posible. Una cosa es poder plantar una bandera gracias a una costosísima misión espacial, otra muy distinta habitar o hacer habitables planetas con temperaturas inhabilitantes, atmósferas con presiones que oscilan entre mortalmente tenue o excesiva, o compuestas por ácido sulfúrico, como es el caso de Venus, el planeta más cercano a la Tierra.

Si miramos más allá del sistema solar nos encontramos que las estrellas más cercanas están a años luz. Además, gracias a la teoría de la relatividad de Albert Einstein, sabemos no tan sólo que la velocidad de la luz no es superable, sino que simplemente no es alcanzable, pues un cuerpo que viajase a dicha velocidad tendría una masa infinita, algo que no es posible. En consecuencia, no creo que el hombre viaje jamás a las estrellas.

Enterprise

El capitán Kirk cumple el requisito indispensable para conocer el Hiperespacio: ser un personaje de ficción.

Nunca hemos percibido ninguna señal de vida fuera de nuestro planeta, por lo que parece razonable suponer que la vida es un rarísimo y especial fenómeno del que sólo tenemos constancia aquí, en esta esfera azul rodeada de un inmenso vacío. Cuando la vida deje de ser posible, nada quedará de ella, ningún rastro ni recuerdo. La única especie que tiene capacidad de cambiarlo es la nuestra, aunque como hemos visto, la realidad del universo juega en nuestra contra para cambiar el destino que le espera a la vida que alberga la Tierra.

Vida: la materia más singular en el cosmos

Vida: la materia más singular en el cosmos.

Ahora bien, los continuos avances que están habiendo en inteligencia artificial podrían, si se mantiene esta evolución a buen ritmo durante décadas, desembocar en un ente capaz de pensar y con conciencia, contenido en microchips si se sigue la evolución de las últimas décadas, o en otros dispositivos futuros que puedan derivar de la computación cuántica u otras alternativas al ordenador actual que puedan surgir en el futuro. Desde luego, teniendo en cuenta el estado actual de la inteligencia artificial y del Machine Learning, es de un gran tecno optimismo suponer que podremos crear una inteligencia comparable a la nuestra.

Esta máquina podría hacer un viaje que no tendría las limitaciones del cuerpo humano respecto al tiempo, a la composición de la atmósfera, ni las restricciones de temperatura. Podría, por lo tanto, hacer un viaje espacial imposible para nosotros, en una nave cuyo diseño se libraría de la dificultad de tener que mantener vivos y en condiciones a seres humanos, dificultad a día de hoy insalvable cuando los tiempos son largos. Simplemente, esta inteligencia artificial podría permanecer suspendida durante miles de años y activarse al llegar a su destino.

Estas máquinas, lanzadas a diferentes estrellas con todo nuestro conocimiento, serían el único legado que quedaría de la vida que una vez hubo en la tierra. Quién sabe si de aquí a millones de años, en otros sistemas no demasiado lejanos al nuestro, otras formas de vida se desarrollen hasta el punto de hacerse nuestras mismas preguntas y puedan desarrollar tecnología orientada a resolverlas, capaz de detectar si en este planeta hay vida, sólo que probablemente para entonces ya no lo habrá.

La vida es algo tan excepcional, no digamos ya la vida inteligente, que resulta remota la posibilidad de que coincidamos en el tiempo y en el espacio. La inteligencia artificial podría aumentar las posibilidades. A día de hoy es ciencia ficción, sí, pero no tanto como el Hiperespacio o convertir a Marte en el jardín del Edén.

Redimensión de imágenes proporcionalmente en PHP

Una necesidad que surge en todas las webs cuyos contenidos son introducidos por un usuario (o varios) a través de un gestor de contenidos, es adaptar las imágenes que estos suben al diseño de la misma para que no se deforme. Además, las proporciones de una imagen en una sección pueden ser diferentes a cómo se muestra en otra; un caso típico es usar una proporción para un listado y otra para la ficha de cada elemento, por ejemplo las noticias, los productos, etc.

Para resolver este problema sirve el conjunto de clases PHP que presentó en este artículo. Este software, a partir de una imagen original, creará una copia por cada tamaño que necesitemos. El proyecto completo puede verse en Github y el objetivo del presente artículo es explicar cómo funciona. En primer lugar, instanciamos la clase:

$objResize = Signia_ImageResize_Factory::getInstanceOf($srcFile, $destFile, $newSize);

Donde:

  • $srcFile es el path hacia la imagen que ha subido el usuario.
  • $desFile es el path de la imagen de destino.
  • $newSize es el tamaño deseado.

A partir de un array con los tamaños deseados podemos crear un bucle por cada uno de ellos ($newSize). Los índices widthMaxheigthMax hacen referencia al tamaño máximo permitido, mientras que width y height pueden entenderse como widthMin y heightMin. Veamos un ejemplo:

$imageType = [
  'slide' => ['width' => 1000, 'widthMax' => 1000, 'height' => 400, 'heightMax' => 400, 'background' => '000000'],
  'r2_34' => ['width' => 468, 'widthMax' => 2340, 'height' => 200, 'heightMax' => 1000],
  'r2_7'  => ['width' => 1080, 'widthMax' => 2700, 'height' => 400, 'heightMax' => 1000],
  'r1_6'  => ['width' => 459, 'widthMax' => 1000, 'height' => 287, 'heightMax' => 625],
  'r1'    => ['width' => 266, 'widthMax' => 266, 'height' => 177, 'heightMax' => 177]
];

En Factory.php podemos ver la clase Signia_ImageResize_Factory:

class Signia_ImageResize_Factory
{
	static public function getInstanceOf($srcImageName, $destImageName, $newSize)
	{
		$aux       = explode(".", $destImageName);
		$extension = end($aux);
		if (preg_match("/jpg|JPG|jpeg|JPEG/", $extension)) {
			$extension = "jpeg";
		}
		$imageResizer = "Signia_ImageResize_" . ucfirst($extension);

		return new $imageResizer($srcImageName, $destImageName, $newSize);
	}
}

Su cometido es invocar la clase que corresponda según la extensión del fichero: Signia_ImageResize_Jpeg, Signia_ImageResize_Gif, etc. Todas ellas tienen el mismo objetivo: crear un recurso imagen a partir del fichero para, más adelante, poder manipular el tamaño de la imagen. Observemos el código de una de ellas, Signia_ImageResize_Jpeg:

class Signia_ImageResize_Jpeg extends Signia_ImageResize_Abstract
{
	function getResizedImage()
	{
		if (!file_exists($this->srcImageName)) {
                    return $this->error_message;
                }
      
                $this->srcImage = @imagecreatefromjpeg($this->srcImageName);
                if ($this->initCheck() && $this->sizeControl()) {
                      $this->destImage = imagecreatetruecolor($this->destWidth, $this->destHeight);
                      if ($this->background) {
                            $this->resizeImageWithBackground();
                      }else{
                           $this->resizeImage();
                      }
                      imagejpeg($this->destImage, $this->destImageName, 50);
         
                      return true;
                }		
	}
}

El tercer parámetro de imagejpeg() es un número entre 0 y 100 que especifica la calidad de la imagen. Obviamente, una mayor calidad implica también un fichero más grande. Google da mucha importancia a la velocidad de carga del sitio a la hora de situarlo entre los resultados de búsqueda, y con 50 se consigue un fichero de peso reducido sin sacrificar demasiada calidad. Esto es especialmente importante si tenemos en cuenta que muchos usuarios descuidan este tema, pudiendo incluso llegar a subir imágenes de más de un 1 MB (siendo esta práctica compatible con la quejas acerca de la velocidad de carga de su sitio).

Si el lector observa las clases Signia_ImageResize_Png y Signia_ImageResize_Gif, comprobará que hay unos extras para que, a pesar de la manipulación de la imagen, no se pierda la transparencia, efecto que sólo puede existir en estos dos formatos de imagen.

class Signia_ImageResize_Gif extends Signia_ImageResize_Abstract
{
   function getResizedImage()
   {
      if (!file_exists($this->srcImageName)) {
         return $this->error_message;
      }
      $this->srcImage = imagecreatefromgif($this->srcImageName);
      if ($this->initCheck() && $this->sizeControl()) {
         $transparency = imagecolortransparent($this->srcImage);
         if ($transparency != -1) {
            $this->destImage    = imagecreatetruecolor($this->aResult[0], $this->aResult[1]);
            $colorTransparent   = imagecolorsforindex($this->srcImage, $transparency);
            $idColorTransparent = imagecolorallocatealpha($this->destImage, $colorTransparent['red'], $colorTransparent['green'], $colorTransparent['blue'], $colorTransparent['alpha']);
            imagefill($this->destImage, 0, 0, $idColorTransparent);
            imagecolortransparent($this->destImage, $idColorTransparent);
         } else {
            $this->destImage = imagecreatetruecolor($this->aResult[0], $this->aResult[1]);
         }
         if ($this->background) {
            $this->resizeImageWithBackground();
         }else{
            $this->resizeImage(true);
         }
         imagegif($this->destImage, $this->destImageName);
         return true;
      }
   }
}

Es desde estas clases donde se llama a la parte más compleja del código, la que realmente redimensiona la imagen: Abstract.php. Básicamente, la imagen que sube el usuario puede diferir en cuanto a las proporciones deseadas en si el ratio de la imagen original es menor o mayor. De no diferenciar el proceso para actuar de forma disitinta en cada caso, algunas imágenes se deformarán, tal y como pasa en los ejemplos que he visto en Stack Overflow sobre el tema. Para entender cómo funciona, bastará con explicar uno sólo de los casos: cuando el destino es mayor. Primero, veamos el contenido de la clase:

abstract class Signia_ImageResize_Abstract
{

   protected $srcImage;
   protected $srcImageName;
   protected $srcWidth;
   protected $srcHeight;
   protected $srcRatio;
   protected $destImage;
   protected $destImageName;
   protected $destWidth;
   protected $destHeight;
   protected $destRatio;
   protected $widthMin;
   protected $heightMin;
   protected $widthMax;
   protected $heightMax;
   protected $background;
   protected $bincolor;
   protected $error;
   public $error_message;
   protected $aMessages = array(
       'src_not_found'      => "El fichero de origen no se encuentra",
       'not_enought_params' => "No se han definido los tamaños de salida",
       'size_values'        => "La imagen es demasiado pequeña para generar las copias",
       'ratio'              => "No se ha podido calcular el ratio destino de la copia"
   );
   protected $aResult   = [];

   public function __construct($srcImageName, $destImageName, $newSize)
   {
      $this->srcImageName   = $srcImageName;
      $this->destImageName  = $destImageName;
      $this->widthMin       = (!isset($newSize['width'])) ? 1 : $newSize['width'];
      $this->heightMin      = (!isset($newSize['height'])) ? 1 : $newSize['height'];
      $this->widthMax       = (!isset($newSize['widthMax']) || $newSize['widthMax'] > 8192) ? 8192 : $newSize['widthMax'];
      $this->heightMax      = (!isset($newSize['heightMax']) || $newSize['heightMax'] > 6144) ? 6144 : $newSize['heightMax'];
      $this->background     = (!isset($newSize['background'])) ? null : $newSize['background'];
      $this->sizeControlled = (!isset($newSize['sizeControlled'])) ? true : $newSize['sizeControlled'];
      if ($this->background) {
         $this->setBincolor();
      }
   }

   public function __destruct()
   {
      if (isset($this->destImage) && is_resource($this->destImage)) {
         imagedestroy($this->destImage);
      }
      if (isset($this->srcImage) && is_resource($this->srcImage)) {
         imagedestroy($this->srcImage);
      }
   }

   /**
    * Si ponemos minWidth y width al mismo valor, e igualamos también minHeight y Height,
    * y la imagen que se sube no es del mismo ratio, saldrán franjas por los lados o arriba y abajo.
    * El background indica el color de estas franjas.
    */
   protected function resizeImageWithBackground()
   {
      if ($this->srcRatio > $this->destRatio) {
         $aux1 = floor($this->srcWidth / $this->destWidth * ($this->destHeight - $this->destWidth / $this->srcWidth * $this->srcHeight));
         $aux2 = floor(($this->destHeight - $this->destWidth / $this->srcWidth * $this->srcHeight) / 2);
         imagecopyresampled($this->destImage, $this->srcImage, 0, 0, 0, -$aux1 / 2, $this->destWidth, $this->destHeight, $this->srcWidth, $this->srcHeight + $aux1);
         $aux3 = imagecolorallocate($this->destImage, $this->bincolor[0], $this->bincolor[1], $this->bincolor[2]);
         imagefilledrectangle($this->destImage, 0, 0, $this->destWidth, $aux2 + 1, $aux3);
         imagefilledrectangle($this->destImage, 0, $this->destHeight - $aux2 - 1, $this->destWidth, $this->destHeight, $aux3);
      } elseif ($this->srcRatio < $this->destRatio) {
         $aux1 = floor($this->srcHeight / $this->destHeight * ($this->destWidth - $this->destHeight / $this->srcHeight * $this->srcWidth));
         $aux2 = floor(($this->destWidth - $this->destHeight / $this->srcHeight * $this->srcWidth) / 2);
         imagecopyresampled($this->destImage, $this->srcImage, 0, 0, -$aux1 / 2, 0, $this->destWidth, $this->destHeight, $this->srcWidth + $aux1, $this->srcHeight);
         $aux3 = imagecolorallocate($this->destImage, $this->bincolor[0], $this->bincolor[1], $this->bincolor[2]);
         imagefilledrectangle($this->destImage, 0, 0, $aux2 + 1, $this->destHeight, $aux3);
         imagefilledrectangle($this->destImage, $this->destWidth - $aux2 - 1, 0, $this->destWidth, $this->destHeight, $aux3);
      }
   }

   /**
    * @param boolean $transparent Para preservar transparencia (imágenes gif y png)
    */
   protected function resizeImage($transparent = false)
   {
      if ($this->srcRatio > $this->destRatio) { // En el original hay más width por cada píxel de height.
         $canvas = imagecreatetruecolor($this->destHeight * $this->srcRatio, $this->destHeight);
         if ($transparent) {
            imagealphablending($canvas, false);
            imagesavealpha($canvas, true);
         }
         $destWidth = $this->destHeight * $this->srcRatio;
         // $canvas contendrá la imagen original con el height del destino y el width recortado proporcionalmente según srcRatio:
         imagecopyresampled($canvas, $this->srcImage, 0, 0, 0, 0, $destWidth, $this->destHeight, $this->srcWidth, $this->srcHeight);
         $auxWidth  = $this->destHeight * $this->srcRatio; // $canvas no es recortada desde x = 0, es decir, no se recorta sólo por la derecha sino también por la izquierda
         imagecopyresampled($this->destImage, $canvas, 0, 0, ($auxWidth - $this->destWidth) / 2, 0, $this->destWidth, $this->destHeight, $this->destWidth, $this->destHeight);
      } elseif ($this->srcRatio < $this->destRatio) { // Menos width por cada height en el original.
         $inverseSrcRatio = $this->srcHeight / $this->srcWidth;
         $canvas          = imagecreatetruecolor($this->destWidth, $this->destWidth * $inverseSrcRatio);
         if ($transparent) {
            imagealphablending($canvas, false);
            imagesavealpha($canvas, true);
         }
         imagecopyresampled($canvas, $this->srcImage, 0, 0, 0, 0, $this->destWidth, $this->destWidth * $inverseSrcRatio, $this->srcWidth, $this->srcHeight);
         $auxHeight = $this->destWidth * $inverseSrcRatio;
         imagecopyresampled($this->destImage, $canvas, 0, 0, 0, ($auxHeight - $this->destHeight) / 2, $this->destWidth, $this->destHeight, $this->destWidth, $this->destHeight);
      } else {
         imagecopyresampled($this->destImage, $this->srcImage, 0, 0, 0, 0, $this->destWidth, $this->destHeight, $this->srcWidth, $this->srcHeight);
      }
   }

   protected function initCheck()
   {
      if (!file_exists($this->srcImageName)) {
         $this->error         = true;
         $this->error_message = $this->aMessages['src_not_found'];
         return false;
      }
      if (!$this->widthMax && !$this->heightMax && !$this->widthMin && !$this->heightMin) {
         $this->error         = true;
         $this->error_message = $this->aMessages['not_enought_params'];
         return false;
      }
      if (!is_resource($this->srcImage)) {
         return false;
      }
      $this->srcWidth  = imagesx($this->srcImage);
      $this->srcHeight = imagesy($this->srcImage);
      $this->error     = false;
      return true;
   }

   protected function sizeControl()
   {
      if (!isset($this->background) && $this->sizeControlled) {
         if ($this->srcWidth < $this->widthMin || $this->srcHeight < $this->heightMin) {
            $this->error         = true;
            $this->error_message = $this->aMessages['size_values'];
            return false;
         }
      }
      // Resolución de incongruencias del tamaño solicitado en $imageType
      if ($this->widthMin > $this->widthMax) {
         $this->widthMin = $this->widthMax;
      }
      if ($this->heightMin > $this->heightMax) {
         $this->heightMin = $this->heightMax;
      }
      // Fin resolución.
      $widthAux  = $this->srcWidth;
      $heightAux = $this->srcHeight;
      //Primero recorta por el ancho:
      if ($this->srcWidth > $this->widthMax) {
         $widthAux  = $this->widthMax;
         $heightAux *= $this->widthMax / $this->srcWidth;
      }
      /* Después por el alto. Si la altura inicial es inferior a la mínima después 
       * de haber sido recortada heightAux = heightMin y widthAux = ...
       */
      if ($heightAux < $this->heightMin) {
         $heightAux = $this->heightMin;
         // Si no, si el alto es superior al máximo, se recorta, volviendo a adaptar el width.
      } elseif ($heightAux > $this->heightMax) {
         if (($this->heightMax / $heightAux) * $widthAux < $this->widthMin) { // Siempre >= 1
            $widthAux = $this->widthMin;
         } else {
            $widthAux *= $this->heightMax / $heightAux;
         }
         $heightAux = $this->heightMax;
      }
      $this->srcRatio   = $this->srcWidth / $this->srcHeight;
      $this->destRatio  = $widthAux / $heightAux;
      $this->destWidth  = (int) $widthAux;
      $this->destHeight = (int) $heightAux;
      return $this;
   }

   private function setBincolor()
   {
      $hexcolor       = str_split($this->background, 2);
      $bincolor[0]    = hexdec('0x{' . $hexcolor[0] . '}');
      $bincolor[1]    = hexdec('0x{' . $hexcolor[1] . '}');
      $bincolor[2]    = hexdec('0x{' . $hexcolor[2] . '}');
      $this->bincolor = $bincolor;
      return $this;
   }
}

Este es un esquema del proceso:

El rectángulo más grande representa la imagen original, mientras el rectángulo más pequeño que contiene representa el tamaño deseado, cada uno tiene representado su ratio o razón en forma de diagonal. En el método resizeImage() se hace, en primer lugar, una redimensión de la imagen en donde la altura pasa a ser la de la imagen de destino y el ancho se adapta proporcionalmente, la imagen resultante se guarda en la variable de tipo recurso $canvas. A continuación, se procede a recortar, pero no desde la esquina inferior izquierda, sino desde un punto x al que se le suma $this->destWidth, de este modo, se recorta la misma cantidad por el lado izquierdo por el derecho. La razón de esto es que las imágenes tienden a tener el objeto principal en el centro.

El esquema también incluye un ejemplo numérico en el que la imagen original tiene un ratio de 1’3 periódico, es decir, unos 13 píxeles de ancho por cada 10 de alto (no tiene mucho sentido hablar de 1’3 píxeles pues es una unididad indivisible), mientras que el ratio deseado es de 0’665.

Cuando el destino tiene un ratio mayor, podemos ver en el siguiente esquema que la idea es la misma, sólo que en el redimensionamiento inicial lo que se adapta proporcionalmente es el alto a partir de un ancho que se fija:

Esquema reducción imagenEl método initCheck() comprueba, en primer lugar, que exista la imagen original para, a continuación comprobar que el array $imageType tiene todos los parámetros esperados. Finalmente, obtiene el ancho y el alto de la imagen original.

El objetivo del método sizeControl() consiste en calcular el ancho y el alto de la imagen de destino, así como el ratio de esta y la original. Previamente, resuelve incongruencias en los parámetros, como, por ejemplo, que el ancho mínimo tolerado (widthMin) sea menor que el máximo (widthMax).

Veamos a continuación un ejemplo: los tamaños r1, r1_6  y slide que aparecen al principio del presente artículo a partir esta imagen:

Júpiter

Hacer click para obtener la imagen original, aunque WordPress redujo algo su peso.

Tamaño r1_6

Tamaño r1_6. Si hacemos click sobre la imagen podremos apreciar que su tamaño coincide con el especificado y que el peso del fichero se ha reducido mucho, más allá de lo que sería proporcional a la reducción de dimensiones, sin afectar, a simple vista, a la calidad de la imagen.

Tamaño r1

Tamaño r1

El comportamiento es diferente cuando, por un lado existe el parámetro background y, por el otro, height y maxHeight valen lo mismo, así como width y maxWidth, pero la imagen subida no es del mismo ratio. En este caso, aparecerán unas franjas horizontales o verticales en el color de este parámetro, lo cual es útil en circuntancias en que queremos que la imagen que se visualiza en la web sea de un tamaño exacto, como podría ser, por ejemplo, en el caso de slides.

Júpiter slide

Generalmente, es conveniente que el color de las franjas coincida con el color de fondo de la web, aunque en este caso se ha escogido el negro.

Generalización de los espacios vectoriales

El conjunto de los vectores libres del plano ℝ2 o del espacio ℝ3, es sólo uno del los muchos objetos matemáticos que pueden formar un espacio vectorial. Este concepto se puede abstraer para englobar no sólo vectores sino también otros objetos como polinomios, funciones o conjuntos de números. En este artículo expondré cómo se realiza esta generalización, en primer lugar para tenerlo yo claro, y en segundo lugar, para ayudar a otros; aunque para este segundo caso debo aclarar al lector que yo no soy matemático y por lo tanto sugiero contrastar lo que aquí expongo con fuentes acreditadas.

Vectores en el espacio

En color amarillo está representado el vector (3, 2, 1) y en violeta el (-1, 0, 2).

Los vectores, tanto en el plano como en el espacio, tienen dirección, sentido y módulo. Si lo pensamos, la abstracción del concepto ya empieza simplemente si nos desplazamos a ℝ4 , pues, ¿cómo representar en este espacio dichos valores? No podemos, resulta que fuera del plano y el espacio tridimensional donde nos introdujeron los vectores en el Bachillerato, el vector ya es una abstracción. La siguiente definición y propiedades nos permitirá ir más allá de ℝ3, e incluso de los vectores, con rigurosidad:

Un espacio vectorial es un conjunto no vacío V de objetos, llamados vectores, donde se definen las operaciones suma y multiplicación por escalares (número reales), sujetos a estos 10 axiomas:

  1. La suma de u y v, u + v, está en V.
  2. Conmutativa: u + v = v + u
  3. Asociativa de la suma: (u + v) + w = u + (v + w)
  4. Elemento neutro de la suma: u + 0 = u
  5. Elemento opuesto: Para cada u en V, existe un -u tal que u + (-u) = 0
  6. El múltiplo escalar de u por c, cu, está en V.
  7. Distributiva I: c(u + v) = cu + cv
  8. Distributiva II: (c + d)u = cu + du
  9. Asociativa del producto: c(du) = (cd)u
  10. Elemento neutro del producto: 1u = u

Estos axiomas deben cumplirse ∀ u, v, w ∈ V, ∀ c, d ∈ ℝ

A continuación, expondré un par de objetos para los que se cumplen todos estos axiomas; veremos también cómo se cumplen los fundamentales, pero no los diez, pues sería excesivamente largo.

Los polinomios

Para n ≥ 0, el conjunto Pn de grado n o menor consiste en todos los polinomios de la forma:

p(t) =  a0 + a1t + a2t + … + antn

Si tenemos otro polinomio:

q(t) =  b0 + b1t + b2t + … + bntn

p + q se define mediante:

(p + q)(t) = p(t) + q(t) = (a0 + b0) + (a1 + b1)t + … + (an + bn)tn

El múltiplo por un escalar, cp, es el polinomio definido así:

(cp)(t) = cp(t) = ca0 + (ca1)t + (ca2)t + … + (cantn)

Los axiomas 1 y 6 han sido satisfechos, ya que p + q y cp son polinomios de grado igual o menor que n. Los axiomas 2, 3 y del 7 al 10 son consecuencia de las propiedades de los números reales. El polinomio 0 actúa como elemento neutro de la suma (axioma 4), mientras que el elemento opuesto es (-1)p. Por lo tanto, Pn es un espacio vectorial.

Las funciones reales definidas en un intervalo

Sea V el conjunto de todas las funciones reales definidas en un conjunto D (ya sea un intervalo o todos los números reales), nos encontramos en un caso parecido al de los polinomios, pues si por ejemplo D = ℝ y tenemos la funciones:

f(t) = cos(t) – 2

g(t) = 1/(t2 + 1) + 7

Se cumple que:

(f + g)(t) = cos(t) + 1/(t2 + 1) + 5

(2g)(t) = 2/(t2 + 1) + 14

En este caso, el vector 0 es la función f(t) = 0 y (-1)f el elemento opuesto de f. Como el cumplimiento de los demás axiomas se deduce de las propiedades de los números reales, V es un espacio vectorial.

Intuyo que, en general, los objetos matemáticos que cumplen las condiciones de linealidad pueden constituir espacios vectoriales pues, por ejemplo, son espacios vectoriales el conjunto de las matrices reales de m filas y n columnas, el conjunto de las funciones derivables en un punto y el de las integrables en un intervalo.

Funciones de orden superior

Tanto en matemáticas como en informática, las funciones de orden superior son aquellas que cumplen, al menos, una de estas condiciones:

  1. Esperan como argumento/s una o más funciones.
  2. Devuelven una función como resultado.

Ejemplos en matemáticas son la derivada y la antiderivada o función primitiva.

operador diferencial

El operador diferencial es una función de orden superior

Antiderivada

La antiderivada de una función f es una función F tal que F’ = f

En informática son la esencia de los lenguajes funcionales, pero también aparecen en lenguajes de otros paradigmas. Este es un ejemplo en el lenguaje Scheme en el que la función (f x) recibe un argumento y devuelve una función:

(define (f x)
  (lambda (y) (+ x y)))
(display ((f 3) 7))

Puede ejecutarse aquí para ver el resultado.

Cuando nació Javascript, a algunos programadores les pareció un lenguaje orientado a objetos fallido1, sobretodo porque, por razones comerciales, se le puso un nombre que lo asocia con Java. Desconozco si su creador estuvo muy de acuerdo con ese nombre pues, tal y como se diseño este lenguaje, da bastante juego a la programación funcional. En el siguiente ejemplo, el método filter() es una función de orden superior, pues espera recibir una función como parámetro:

function isPrime(x){
  if (x === 2) {
     return true;
  }
  let test = x%2 !== 0;
  let i = 3;
  stop = Math.floor(Math.sqrt(x)); // Raíz entera de x
  while (test && i <= stop) {
	  test = x%i !== 0;
	  i = i + 2;
  }
  return test;
}

const numbers = [47, 139, 137, 213, 2, 3, 45, 1515];
const primeNumbers = numbers.filter(isPrime);
console.log(primeNumbers);

Lo que este programa hace es filtrar la formación de números naturales “numbers“, dejando sólo los que sean primos en “primeNumbers“. Cada elemento de “numbers” será evaluado por la función “isPrime” mediante la criba de Eratóstenes. El lector puede ejecutarlo accediendo a la consola del navegador pulsando F12 y modificar el valor de “numbers” con los números (o el número) que quiera saber si son primos o no.

Este tipo de funciones están en prácticamente todos los lenguajes modernos, incluso en los que no se tuvo en cuenta el paradigma funcional en el momento de su creación. Es el caso de PHP, donde podemos encontrar una gran cantidad de funciones que esperan otra función, como es el caso de, por ejemplo, preg_replace_callback()2:

$capitalice = function($coincidencia) {
    return strtoupper($coincidencia[1]);
};

echo preg_replace_callback('~-([a-z])~', $capitalice, 'hola-mundo');

Además de usar las implementadas en funciones y métodos propios del lenguaje, también podemos crear las nuestras, de forma parecida a un lenguaje completamente funcional. En Javascript, la Wikipedia nos ofrece el siguiente ejemplo:

const twice = (f, v) => f(f(v));
const add3 = v => v + 3;

console.log(twice(add3, 7));

Lo mismo es posible en PHP:

$twice = function($f, $v) {
    return $f($f($v));
};

$f = function($v) {
    return $v + 3;
};

echo($twice($f, 7));

La programación funcional pretende tratar la programación como la evaluación de funciones matemáticas, paradigma muy diferente a la programación imperativa, basada en estados y en instrucciones que lo cambian. Tal vez las características funcionales que tienen algunos lenguajes puedan ayudarnos a introducirnos en un paradigma, el funcional, que nos exige una forma muy distinta de enfocar los problemas.


 

1 Todavía hoy en día, y a pesar de los cambios que ha sufrido en los últimos ECMA, sigue despertando las críticas de los programadores que, debido a su nombre, esperan que se comporte como un lenguaje completamente orientado a objetos, como Java, y se dan de bruces contra la realidad.

2 El parámetro que recibe la función contenida en $capitalize son las coincidencias que encuentre la expresión regular.