Mes: agosto 2022 (Página 2 de 3)

Funciones Matemáticas

En estas páginas encontraras el material necesario para comprender las diferentes Funciones Matemáticas, además de un catalogo completo con sus correspondientes usos y aplicaciones.

Si quieres revisar temas básicos de matemáticas antes de continuar te invitamos a dar Clic aquí y explorar las operaciones básicas en matemáticas.

1. Generalidades de las funciones

Las funciones aparecen cuando existen cantidades que dependen unas de las otras, normalmente se ha definido una función como una regla que asigna a cada elemento $x$ de un conjunto $A$ exactamente un elemento llamado $f(x)$f de otro conjunto $B$. En este punto tenemos algunas definiciones convenientes que realizar en el manejo de las funciones:

  • Dominio: este concepto corresponde al mismo conjunto $A$
  • Rango: este concepto corresponde al mismo conjunto $B$
  • Variable independiente: variable que representa un elemento arbitrario del conjunto $A$ (dominio)
  • Variable dependiente: variable que representa un elemento en el rango de $f$

Las funciones matemáticas se pueden representar de distintas maneras incluyendo:

  • Verbalmente (usando una descripción en prosa), numéricamente (con una tabla de valores)
  • Visualmente (con una gráfica) o algebraicamente (con una fórmula o ecuación).

La última es la forma más habitual y conveniente para expresar funciones, así mismo se puede visualizar la función recurriendo a la visualización como regla de asignación entre conjuntos, más formalmente tendríamos que si $f$ es una función cuyo dominio es $A$ su gráfica son las parejas ordenadas

$\{(x,f(x))x \in A\}$

Y su correspondiente diagrama de flechas es como el de la figura 1.

Diagrama de flechas para funciones
Figura 1. Diagrama de flechas para funciones

1.1. Prueba de recta vertical

Ahora bien, es importante notar que la definición de función implica que la asignación del conjunto del dominio al conjunto del rango, no realice asignaciones de forma tal que un mismo elemento del dominio asigne dos valores diferentes del rango, gráficamente esto se puede ver fácilmente mediante la Prueba de la recta vertical de tal manera que si se traza una línea vertical en el gráfico de una función esta corte la gráfica en un solo punto. De tal manera, la gráfica 2 corresponde a una parabola pero no a una función.

Gráfico de una curva que no es función
Figura 2. Gráfico de una curva que no es función

1.2. Funciones por secciones

Otra forma de definir funciones es utilizando la definición de funciones matemáticas por secciones, de estas funciones la más conocida es la función valor absoluto, la cual se muestra en la figura 3. De esta forma se puede definir funciones de una manera muy flexible, por ejemplo la función escalón es una de ellas.

Función valor absoluto
Figura 3. Función valor absoluto

1.1. Simetría 

Existen dos tipos de simetría para las Funciones Matemáticas, la simetría par y la simetría impar. La primera es cuando se cumple la condición f(−x)=f(x)f(-x)=f(x) y la segunda f(−x)=−f(x)f(-x)=-f(x), gráficamente se pueden observar estas simetrías siendo la simetría par una simetría respecto al eje yy en una gráfica en el plano cartesiano y la simetría impar una simetría respecto al origen en el plano cartesiano, las figuras 4 y 5 muestras estas simetrías respectivamente.

Simetría par
Figura 4. Simetría par
Simetría impar
Figura 5. Simetría impar

1.2. Funciones Matemáticas crecientes y decrecientes 

Estas dos definiciones permiten ver el comportamiento de una función en un intervalo del dominio II, de tal manera que una función es

  • Creciente: si se cumple que $f(x_1) \lt f(x_2)$ siempre que $x_1 \lt x_2$ en $I$
  • Decreciente: si se cumple que $f(x_1) \gt f(x_2)$ siempre que $x_1 \gt x_2$ en $I$

Las gráficas 6 y 7 muestran respectivamente funciones crecientes y decrecientes.

Función creciente
Figura 6. Función creciente
Función decreciente
Figura 7. Función decreciente

2. Transformación de Funciones

Las transformaciones de Funciones Matemáticas son muy útiles, puesto que permiten construir funciones que a primera mano parecen muy complejas usando funciones básicas, lo anterior ayuda a imaginarnos las gráficas de las funciones con las que se trabaja fácilmente.

2.1. Desplazamientos verticales y horizontales 

A continuación se listan los diferentes desplazamientos que se pueden realizar partiendo de una constante $c \gt 0$ y la función $y=f(x)$

  • Hacia abajo: $y=f(x)−c$
  • Hacia arriba: $y=f(x)+c$
  • Hacia la izquierda: $y=f(x+c)$
  • Hacia la derecha: $y=f(x−c)$

2.2. Alargamientos y reflexiones 

A continuación se listan los diferentes operaciones de alargamiento y compresión que se pueden realizar partiendo de una constante $c \gt 1$ y la función $y=f(x)$

  • Alargamiento vertical: $y=cf(x)$
  • Compresión vertical: $y=(\frac{1}{c})f(x)$
  • Compresión horizontal: $y=f(cx)$
  • Alargamiento vertical: $y=f(\frac{x}{c})$
  • Reflexión respecto a x: $y=−f(x)$
  • Reflexión respecto a y: $y=f(−x)$

2.3. Algebra de funciones 

Las funciones se pueden operar usando los operadores algebraicos tradicionales para obtener nuevas funciones, de forma similar existe una operación llamada composición de funciones estas operaciones se muestran a continuación para las funciones $f(x)$ y $g(x)$

  • Suma de funciones: $(f+g)(x)=f(x)+g(x)$ dominio $A \cap B$
  • Resta de funciones: $(f−g)(x)=f(x)−g(x)$ dominio $A \cap B$
  • Multiplicación de funciones: $(fg)(x)=f(x)g(x)$ dominio $A \cap B$
  • División de funciones: $(\frac{f}{g})(x)=\frac{f(x)}{g(x)}$ dominio ${x \in A \cap B | g(x) \ne 0}$
  • Composición de funciones: $f(u)=f(g(x))$ que también se escribe como $(f \circ g)(x)=f(g(x))$

3. Curvas paramétricas

En ocasiones las funciones pueden escribirse en términos de sus componentes en los ejes $x$ e $y$, para ellos se expresan en dos ecuaciones como sigue:

$x=f(t) \text{ ; } y=g(t)$

La variable $t$ se conoce como parámetro. Es importante notar que estas expresiones pueden dar lugar a un concepto más general que el de función debido a que si se despeja $t$ en las ecuaciones anteriores y se igualan el resultado puede no superar la prueba de la línea vertical y como consecuencia obtener una relación, para ello se utiliza el término más general curva. Considere como ejemplo el caso de las curvas paramétricas de un círculo de centro $(h,k)$ y radio $r$:

$x=h+r\sin(t) \text{ ; } y=k+r\cos(t)$

4. Funciones inversas

Antes de definir la función inversa, definamos una función uno a uno (ver correspondencia de funciones) como aquella función que nunca adopta el mismo valor dos veces:

$f(x_1) \ne f(x_2) \text{ siempre que } x_1 \neq x_2$

Otra forma de definir si una función es uno a uno es a través de la prueba de la recta horizontal, en este sentido una recta horizontal trazada sobre la gráfica de la función solamente la puede cortar una sola vez. Se define la función inversa siendo $f$ una función uno a uno con dominio $A$ y rango $B$, luego la función inversa $f^{−1}$ tiene rango $A$ y dominio $B$ y se define mediante:

$f^{-1}(y)=x \Leftrightarrow f(x)=y$

La función inversa implica las siguientes expresiones:

$f^{-1}(f(x))=x \text{ ; }\forall x \in A$

$f(f^{-1}(x))=x \text{ ; }\forall x \in B$

Para hallar la función inversa se debe resolver a xx en término de yy siempre y cuando sea posible.

5. Correspondencia de funciones

A continuación se estudia las principales correspondencias de funciones, como son: unívoca, biunívoca, inyectiva, biyectiva, sobreyectiva; las primeras correspondencias entre funciones (unívoca, biunívoca) tratan los conceptos más generales aplicados a conjuntos, pero también pueden entenderse desde el punto de vista de las funciones y en este caso las correspondencias se denominan aplicaciones, teniendo los casos inyectiva, biyectiva y sobreyectiva que se resumen a continuación. Considere $A$ el dominio de una función $f$ cuyo rango es $B$

5.1. Funciones inyectivas

Una función es inyectiva si a elementos diferentes del dominio asigna elementos diferentes del rango, formalmente las funciones inyectivas se definen como:

$a\text{ , }a’ \in A\text{ y }a \neq a’\text{ entonces }f(a) \neq f(a’)$

5.2. Funciones sobreyectivas

Las funciones sobreyectivas también son denominadas funciones suprayectivas, epiyectivas, suryectiva, exhaustiva o subyectivas y se definen como aquellas funciones que para todo número en el rango tienen un correspondiente valor en el dominio, formalmente se define como:

$\forall b \in B \text{ } \exists \text{ } a \in A \text{ con }f(a)=b$

La función también puede ser no sobreyectiva si la anterior premisa no se cumple.

6.3. Funciones biyectivas

Estas funciones también se denominan uno a uno, y son aquellas que son inyectivas y sobreyectivas al mismo tiempo.

6. Artículos de Interés

Geometría Vectorial

La Geometría Vectorial es muy utilizada cuando se inicia el estudio del Álgebra Lineal o en el cálculo, que de hecho existe una rama llamada Cálculo Vectorial muy útil para temas en física como electromagnetismo. Esperamos que esta página sirva de referencia y estudio.

Si quieres avanzar al mundo del cálculo te invitamos a dar Click en este enlace.

1. Vectores

Un vector es un segmento de recta dirigido que se caracteriza por tener magnitud y dirección. También podemos definir un vector como una cantidad que tiene magnitud y dirección.Todos los vectores que tengan la misma magnitud y dirección se denominan equivalentes sin importar su punto inicial. Note que un segmento de recta dirigido se puede caracterizar dando su punto inicial y final. El segmento de recta se puede representar geométricamente como:

$\overrightarrow{PQ}$

y algebraicamente como $\mathbf v$. El vector de forma algebraica se suele representar con su punto inicial situado en el origen $\overrightarrow{OR}$ de tal forma que estaría especificado por sus coordenadas $(a,b)$, a estos números se les llama elementos o componentes del vector. Nota: la definición anterior aplica a las coordenadas $(x,y)$ en el plano. El vector cero denotado como $\mathbf 0$ es el único vector que no tiene una dirección específica.

2. Magnitud y dirección

En Geometría Vectorial la magnitud y dirección de un vector se pueden calcular fácilmente para vectores en el plano usando las siguientes relaciones geométricas:

$\text{magnitud de } \mathbf v=|\mathbf v|=\sqrt{a^2+b^2}$

$\text{dirección de } \mathbf v=\tan(\theta)=\frac{a}{b}$

3. Operaciones con vectores

A continuación se muestran las operaciones más habituales con vectores, para ello consideremos $\mathbf v$, $\mathbf u$ y $\alpha$ dos vectores y un escalar respectivamente.

  • Multiplicación por un escalar: al multiplicar por un escalar la magnitud del vector se multiplica por el valor absoluto del escalar, así: $\alpha \mathbf v=(\alpha a, \alpha b)=| \alpha || \mathbf v |$. En cuanto a la dirección si $\alpha$ es positivo se conserva sin cambios y si $\alpha$ es negativo esta se ve incrementada en $\pi$. Lo anterior conlleva a que dos vectores sean paralelos si uno es múltiplo escalar del otro.
  • Suma de dos vectores: la suma de dos vectores es: $\mathbf v + \mathbf u = (a_1+a_2,b_1+b_2)$
  • Resta de dos vectores: la resta de dos vectores es: $\mathbf v – \mathbf u = (a_1-a_2,b_1-b_2)$

Gráficamente estas operaciones se pueden representar como se ilustra en la siguiente figura:

Suma y resta de vecores
Figura 1. Suma y resta de vectores

Desigualdad del triangulo Esta es una desigualdad muy utilizada y común en el estudio de las matemáticas y establece lo siguiente en cuanto a los vectores:

$|\mathbf{u+v}| \leq |\mathbf{u}| + |\mathbf{v}|$

4. Vectores en el espacio y representaciones

Aunque muchos de los conceptos aplicados a $\mathbb R^2$ aplican igualmente a $\mathbb R^3$ como son: suma, multiplicación por escalar, vector unitario, y ángulo entre vectores, se detallarán algunas de las operaciones más relevantes. En este caso los puntos en el espacio están determinados por tres coordenadas respecto a los ejes coordenados $x$, $y$ y $z$, así $(a,b,c)$, de tal forma que un vector $\overrightarrow{PQ}$ tiene una magnitud que se puede calcular con la fórmula de distancia más general:

$\overline{PQ}=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}$

De manera similar la suma y multiplicación por un escalar están dados como sigue:

$\mathbf u \pm \mathbf v=(x_1 \pm x_2, y_1 \pm y_2, z_1 \pm z_2)$

$\alpha \mathbf u=(\alpha x_1, \alpha y_1, \alpha z_1)$

Las siguientes son las operaciones y propiedades que tienen los vectores, siendo $\mathbf a$ y $\mathbf b$ vectores y $c$ y $d$ escalares:

  • Suma y resta: se usa la ley del triángulo o la ley del paralelogramo
  • Multiplicación por un escalar: escala el tamaño del vector, en caso que el escalar sea un número negativo el vector cambia de dirección y presenta un desfase de 180º
  • $\mathbf{a}+\mathbf{b}=\mathbf{b}+\mathbf{a}$
  • $\mathbf{a}+\mathbf{0}=\mathbf{a}$
  • $c(\mathbf{a}+\mathbf{b})= c\mathbf{a} +c \mathbf{b}$
  • $(cd)\mathbf{a}=c(d\mathbf{a})$
  • $\mathbf{a}+(\mathbf{b}+\mathbf{c})=(\mathbf{a}+\mathbf{b})+\mathbf{c}$
  • $\mathbf{a}+(-\mathbf{a})=\mathbf{0}$
  • $(c+d)\mathbf{a}=c\mathbf{a}+d\mathbf{a}$
  • $1\mathbf{a}=\mathbf{a}$

Un vector que tenga su punto inicial en el origen y el punto final sea $P$ se llama vector posición del punto $P$. Es importante tener en cuenta que como un vector se define por su magnitud y dirección bien sea en el plano bidimensional o en el espacio tridimensional, no importa el punto de origen, es decir un mismo vector puede tener muchas representaciones simplemente cambiando su punto de origen, por ello se denota:

$\mathbf{a}=\lt a_1,a_2 \gt$

Al vector posición cuyo punto final es $P(a1,a2)$ por lo tanto es importante distinguir el punto final, del vector posición de ese punto. Ahora bien, dado los puntos $A(x1,y1,z1)$ y $B(x2,y2,z3)$ el vector $\mathbf a$ con representación $\overrightarrow {AB}$ es:

$\mathbf{a}=\lt x_2-x_1,y_2-y_1,z_2-z_1 \gt$

La longitud o magnitud de un vector $\mathbf{a}=\lt a_1,a_2 \gt$ se puede calcular con la fórmula de la distancia:

$|\mathbf{a}|=\sqrt{a_1^2+a_2^2+a_3^2}$

Y algebraicamente se puede definir la suma (análogamente la resta) de los vectores y la multiplicación por un escalar como sigue:

$\lt a_1,a_2,a_3 \gt + \lt b_1,b_2,b_3 \gt = \lt a_1+b_1,a_2+b_2,a_3+b_3 \gt$

$c \lt a_1,a_2,a_3 \gt = \lt ca_1,ca_2,ca_3 \gt$

Podemos escribir los siguientes tres vectores en el espacio, los cuales son muy útiles puesto que permiten representar cualquier vector como combinación lineal de ellos, es decir, estos vectores forman una base para el espacio. Estos vectores son los siguientes:

$\mathbf{i}= \lt 1,0,0 \gt$

$\mathbf{j}= \lt 0,1,0 \gt$

$\mathbf{k}= \lt 0,0,1 \gt$

Estos son los denominados vectores básicos canónicos. Es importante notar que estos vectores son unitarios, es decir su magnitud es la unidad. El vector unitario que tiene la misma dirección que $\mathbf a$ que se denominará $\mathbf u$ se puede calcular como:

$\mathbf{u}=\frac{1}{|\mathbf{a}|}\mathbf{a}=\frac{\mathbf{a}}{|\mathbf{a}|}$

5. Dirección de un vector

La dirección de un vector está dada por la dirección del vector unitario correspondiente. Adicionalmente se puede calcular los ángulos que forma el vector con los ejes coordenados, en este caso con $x$, $y$ y $z$; para tal fin se definen respectivamente los ángulos $\alpha$, $\beta$, $\gamma$, las siguientes relaciones permiten calcular estos ángulos:

$\cos \alpha=\frac{x_0}{|\mathbf v|} \text{ } \cos \beta=\frac{y_0}{|\mathbf v|} \text{ } \cos \gamma=\frac{z_0}{|\mathbf v|}$

Es importante advertir la siguiente relación:

$\cos^2 \alpha + \cos^2 \beta + \cos^2 \gamma = 1$

Estos cosenos se denominan cosenos directores y de forma análoga para el vector $\mathbf v=(a,b,c)$ siempre y cuando $|\mathbf v|≠1$ los números $a$, $b$, $c$ se denominan números directores del vector.

6. Vectores unitarios

Dos vectores muy importantes son los vectores $\mathbf i$ y $\mathbf j$, estos vectores de magnitud uno apuntan en dirección de los ejes coordenados $x$ e $y$ respectivamente, en terminología de álgebra lineal estos vectores son linealmente independientes que forman una base para $\mathbb R^2$. Estos vectores son muy útiles porque permiten escribir otros vectores en términos de sus componentes horizontales y verticales como lo muestra la siguiente expresión:

$\mathbf v = a \mathbf i + b \mathbf j$

Definamos vector unitario como aquel vector que tiene longitud 1, se puede escribir por tanto de las siguiente forma:

$\mathbf u = (\cos\theta)\mathbf i + (\sin\theta)\mathbf j=\frac{\mathbf v}{|\mathbf v|}$

7. Producto escalar y proyecciones

El producto escalar entre dos vectores se define como:

$\mathbf u \cdot \mathbf v = a_1a_2+b_1b_2$

Note que el resultado del producto escalar es un número y está denotado con un punto. Así mismo es necesario definir el ángulo entre dos vectores como el ángulo no negativo más pequeño entre las representaciones de los vectores teniendo ambos el mismo punto de origen, el ángulo entre los vectores se denota como $\varphi$. Como consecuencia del producto vectorial nos podemos encontrar con los siguientes resultados:

$|\mathbf v|^2=\mathbf v \cdot \mathbf v$

Siendo $\varphi$ el ángulo entre vectores:

$\cos \varphi = \frac{\mathbf u \cdot \mathbf v}{|\mathbf u||\mathbf v|}$

De tal forma que el producto escalar también se puede expresar en términos del ángulo entre vectores, así:

$\mathbf v \cdot \mathbf v=|\mathbf u||\mathbf v|\cos \varphi$

8. Vectores paralelos

Dos vectores son paralelos si el ángulo entre ellos es 0 o 180º. Dos vectores paralelos pueden tener la misma dirección o direcciones opuestas. De esta forma dos vectores $\mathbf u$ y $\mathbf v$ son paralelos implica que $\mathbf v = \alpha \mathbf u$ para alguna constante $\alpha$ y siempre y cuando ambos vectores sean diferentes de cero.

9. Vectores ortogonales

Dos vectores $\mathbf u$ y $\mathbf v$ son ortogonales siempre y cuando sean diferentes de cero, y el ángulo entre ellos sea cero, lo anterior implica que si son ortogonales se cumple que:

$\mathbf u \cdot \mathbf v = 0$

10. Proyección

Las proyecciones vectoriales son muy útiles en diversos cálculos. Para poder derivar el resultado de una proyección vectorial se debe partir del siguiente resultado:

$\mathbf w=\mathbf u-\frac{(\mathbf u \cdot \mathbf v)}{|\mathbf v|^2}\mathbf v$

Donde $\mathbf u$ y $\mathbf v$  son vectores cualesquiera y el vector$\mathbf w$  resultante es ortogonal a $\mathbf v$  De esta forma para los vectores $\mathbf u$  y $\mathbf v$  la proyección de $\mathbf u$  sobre $\mathbf v$  es el vector denotado por $proy_{\mathbf v}\mathbf u$ y definido como:

$proy_{\mathbf v}\mathbf u=\frac{(\mathbf u \cdot \mathbf v)}{|\mathbf v|^2}\mathbf v$

La componente de $\mathbf u$ en dirección de $\mathbf v$ es:

\frac{(\mathbf u \cdot \mathbf v)}{|\mathbf v|^2}

y $c$ es un escalar. La siguiente imagen ilustra esta operación de proyección.

Proyección vectorial
Figura 2. Proyección vectorial

11. Producto punto o producto escalar

La definición de producto escalar se amplía en este caso de la siguiente manera:

$\mathbf u \cdot \mathbf v = a_1a_2+b_1b_2+c_1c_2=|\mathbf u||\mathbf v|\cos \varphi$

Las propiedades del producto punto se resumen a continuación. Siendo $\mathbf a$, $\mathbf b$ y $\mathbf c$ vectores en $V_3$ y $c$ un escalar:

  • $\mathbf a \cdot \mathbf a = | \mathbf a |^2$
  • $\mathbf a \cdot \mathbf b = \mathbf \cdot \mathbf a$
  • $\mathbf a \cdot (\mathbf b + \mathbf c) = \mathbf a \cdot \mathbf b + \mathbf a \cdot \mathbf c$
  • $(c \mathbf a) \cdot \mathbf b = c (\mathbf a \cdot \mathbf b)$
  • $\mathbf 0 \cdot \mathbf a = 0$

De cualquier forma las relaciones vistas para el cálculo de proyecciones siguen siendo las mismas.

12. Producto cruz

Note que el producto punto o producto escalar no es cerrado bajo el dominio de los vectores, la operación que se definirá a continuación conocida como producto cruz o producto vectorial si es cerrada y su resultado es un vector perpendicular (ortogonal) a los dos vectores que hagan parte del producto, por esta razón el producto cruz no esta definido para $\mathbb R^2$. A continuación se presenta la definición:

$\mathbf u \times \mathbf v=(b_1c_1-c_1b_2)\mathbf i+(c_1a_2-a_1c_2)\mathbf j+(a_1b_1-b_1a_2)\mathbf k$

Donde $\mathbf i$, $\mathbf j$ y $\mathbf k$ son vectores unitarios en las direcciones positivas de los ejes coordenados. Alternativamente, teniendo presente que los vectores tengan componentes $\mathbf u = <a_1, a_2, a_3>$ y $\mathbf v = <b_1,b_2,b_3>$ se puede contar con una definición basada en determinantes, así:

$\mathbf u \times \mathbf v=\begin{vmatrix} \mathbf i & \mathbf j & \mathbf k \\ a_1 & a_2 & a_3 \\ b_1 & b_2 & b_3 \end{vmatrix}$

El resultado de este producto es un vector ortogonal a $\mathbf u$ y $\mathbf v$ cuya dirección está determinada por la regla de la mano derecha. Las propiedades del producto cruz se muestran a continuación. Sean $\mathbf u$, $\mathbf v$, $\mathbf w$ tres vectores en $\mathbb R^3$ y $\alpha$ un escalar entonces:\mathbf u \times \mathbf 0 = \mathbf 0 \times \mathbf u = \mathbf 0

  • $\mathbf u \times \mathbf v = -(\mathbf v \times \mathbf u)$
  • $\mathbf u \times \mathbf 0 = \mathbf 0 \times \mathbf u = \mathbf 0$
  • $\mathbf u \times \mathbf v = -(\mathbf v \times \mathbf u)$
  • $(\alpha \mathbf u) \times \mathbf v=\alpha(\mathbf u \times \mathbf v$
  • $\mathbf u \cdot (\mathbf u \times \mathbf v)=\mathbf v \cdot (\mathbf u \times \mathbf v)=0$
  • Si $\mathbf u$ y $\mathbf v$ son diferentes de cero serán paralelos si y sólo si $\mathbf u \times \mathbf v=\mathbf 0$

Otra relación útil para calcular el producto vectorial se obtiene con la siguiente expresión que permite calcular la magnitud del producto, teniendo en cuenta que la dirección es perpendicular a los vectores del producto y esta dada por la regla de la mano derecha:

$|\mathbf u \times \mathbf v|=|\mathbf u||\mathbf v|\sin\varphi$

Se puede interpretar la magnitud del vector resultante como el área del paralelogramo que tiene lados adyacentes $\mathbf u$ y $\mathbf v$. De forma similar la magnitud el triple producto escalar corresponde al área del paralelepipedo definido por los vectores $\mathbf u$, $\mathbf v$ y $\mathbf w$

13. Triples productos

Combinando las operaciones anteriores de productos punto y producto cruz podemos obtener relaciones interesantes como son el triple producto escalar y el triple producto vectorial.

El triple producto escalar para tres vectores $\mathbf a$, $\mathbf b$ y $\mathbf c$ es el siguiente:

$\mathbf a \cdot (\mathbf b \times \mathbf c)$

Y en representación matricial se puede ver que su valor es el siguiente determinante:

$\mathbf a \cdot (\mathbf b \times \mathbf c)=\begin{vmatrix} a_1 & a_2 & a_3 \\ b_1 & b_2 & b_3 \\ c_1 & c_2 & c_3 \end{vmatrix}$

Una propiedad útil es la siguiente:

$(\mathbf a \times \mathbf b) \cdot \mathbf c = \mathbf a \cdot (\mathbf b \times \mathbf c)$

Su magnitud se puede interpretar como el volumen del paralelepipedo cuya base es el paralelograma definido por el producto cruz y su altura es la proyección del vector $\mathbf a$ sobre el vector resultante del producto cruz.

Por otra parte el triple producto vectorial se define como el producto cruz de los tres vectores, lo cual se puede expandir de la siguiente manera:

$\mathbf a \times (\mathbf b \times \mathbf c) = (\mathbf a \cdot \mathbf c) \mathbf b – (\mathbf a \cdot b) \mathbf c$

13. Artículos de Interés

Matrices en Álgebra Lineal

En esta página encontrarás un desglose rápido y útil de de los temas básicos y más utilizados para llegar a las Matrices en Álgebra Lineal y poder avanzar a temas más retadores en álgebra.

Si buscas material acerca de cálculo límites, derivadas e integrales comienza dando Click aquí.

1. Vectores, Matrices en Álgebra Lineal y Sistemas de Ecuaciones

En el estudio de Las Matrices en Álgebra Lineal aparecen los vectores y las matrices, los primeros como un arreglo de números de una sola dimensión y los segundos como un arreglo de números de más de una dimensión.

1.1 Vectores

Vectores como los siguientes son denominados vectores fila y vectores columna respectivamente.

$\begin{bmatrix} \alpha & \beta & \gamma \end{bmatrix}$

$\begin{bmatrix} \alpha \\ \beta \\ \gamma \end{bmatrix}$

Es importante anotar que un vector es un conjunto ordenado de elementos y cada una de sus posiciones también se denomina elemento o componente. Este arreglo de elementos puede tener un tamaño determinado y sirve para agrupar un conjunto de valores que están interrelacionados, existen muchas propiedades relevantes de los vectores y las operaciones comunes de aritmética también están disponibles con ellos.

Las componentes de los vectores pueden ser reales $\mathbb R$ o complejos $\mathbb C$ de tal manera un vector de $n$ componentes reales representa el espacio $\mathbb R^n$, así $\mathbb R^2$ se llaman vectores en el plano y $\mathbb R^3$ se llaman vectores en el espacio.

1.1.1. Producto vectorial

Este producto está definido por la combinación lineal de dos vectores, de manera que el resultado de esta operación es un valor escalar, dado por la sumatoria de la multiplicación de las correspondientes componentes, de manera que una multiplicación vectorial (o producto punto) de dos vectores es compatible solo si ambos componentes tienen igual tamaño. La combinación lineal está dada por la siguiente sumatoria

$\mathbf a\cdot\mathbf b=\sum_{i=1}^na_ib_i$

1.1.2. Propiedades de los vectores

$\mathbf a\cdot\mathbf 0=0$

$\mathbf a\cdot\mathbf b=\mathbf b\cdot\mathbf a$

$\mathbf a\cdot(\mathbf b+\mathbf c)=\mathbf a\cdot\mathbf b+\mathbf a\cdot\mathbf c$

$(\alpha\mathbf a)\cdot\mathbf b=\alpha(\mathbf a\cdot\mathbf b)$

2. Matrices en Álgebra Lineal

Las Matrices en Álgebra Lineal aparecen, por ejemplo en el estudio de ecuaciones simultáneas, puede considerar para el ejemplo un sistema de ecuaciones de dos incógnitas con dos ecuaciones, que puede presentar los siguientes casos y a través de las siguientes ecuaciones:

$a_{11}x_1+a_{12}x_2=b_1$

$a_{21}x_1+a_{22}x_2=b_2$

Donde su correspondiente matriz de coeficientes es:

$\begin{bmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{bmatrix}$

1. Una única solución, las rectas se intersecan en un solo punto, como muestra la siguiente gráfica

Sistemas de ecuaciones única solución
Figura 1. Única solución

2. Las rectas nunca se intersecan, por lo tanto, no se tiene solución

Sistemas de ecuaciones sin solución
Figura 2. Sin solución

3. Las rectas coinciden en todos los puntos y, por lo tanto, se tienen infinitas soluciones

Sistemas de ecuaciones infinitas soluciones
Figura 3. Infinitas soluciones

Estos sistemas de ecuaciones se pueden representar a través de matrices como sigue:

$\mathbf A \cdot x=b$

Donde $\mathbf A$ es la matriz de coeficientes de las ecuaciones, $x$ es un vector columna con las variables y $b$ es el vector columna con los valores de los coeficientes independientes. Cuando el sistema genérico de $n$ ecuaciones con mm incógnitas no presenta ninguna solución se dice que es un sistema inconsistente, por otro lado, si tiene al menos una solución se dice que es un sistema consistente.

2.1. Producto matricial

Este producto es compatible entre dos Matrices en Álgebra Lineal siempre y cuando el número de columnas de la primera matriz sea el mismo número de filas de la segunda matriz. Esta multiplicación no es conmutativa y el resultado de multiplicar dos matrices $\mathbf A$ y $\mathbf B$ esta dado por:

$c_{ij}=(\text{reglón }i \text{ de } \mathbf A)\cdot(\text{columna }j \text{ de } \mathbf B)$

2.2. Propiedades de las matrices

$\mathbf A+\mathbf 0=\mathbf A$

$\mathbf 0\mathbf A=\mathbf 0$

$\mathbf A+\mathbf B=\mathbf B+\mathbf A$

$(\mathbf A+\mathbf B)+\mathbf C=\mathbf A+(\mathbf B+\mathbf C)$

$\alpha(\mathbf A+\mathbf B)=\alpha\mathbf A+\alpha\mathbf B$

$\mathbf I\mathbf A=\mathbf A$

$ (\alpha + \beta)\mathbf A=\alpha\mathbf A+\beta\mathbf A$

$ \mathbf A(\mathbf B\mathbf C)=(\mathbf A\mathbf B)\mathbf C$

$\mathbf A(\mathbf B+\mathbf C)=\mathbf A\mathbf B+\mathbf A\mathbf C$

$(\mathbf A+\mathbf B)\mathbf C=\mathbf A\mathbf C+\mathbf B\mathbf C$

3. Artículos de Interés

Matemáticas Operativas

En esta página encontraras conceptos fundamentales de Matemáticas Operativas que incluyen relaciones básicas de trigonometría y algunos temas utilizados en situaciones más avanzadas como fracciones parciales.

Navega a este link para avanzar a temas de cálculo, los límites es un buen punto para iniciar.

1. Trigonometría

Algunas de las relaciones trigonométricas mas útiles en matemáticas operativas, relacionadas con las funciones trigonométricas básicas como el seno, el coseno y la tangente se listan a continuación:

$\sin\theta=\frac{1}{\csc\theta}\text{ ; }\cos\theta=\frac{1}{\sec\theta}\text{ ; }\tan\theta=\frac{1}{\cot\theta}$

Las relaciones en un triangulo rectángulo donde $h$ representa la hipotenusa, $co$ el cateto opuesto y $ca$ el cateto adyacente del ángulo $\theta$ están dadas por las siguientes relaciones

Triangulo rectángulo
Figura 1. Triangulo rectángulo

$\sin\theta=\frac{co}{h}\text{ ; }\cos\theta=\frac{ca}{h}\text{ ; }\tan\theta=\frac{co}{ca}=\frac{\sin\theta}{\cos\theta}$

Algunas otras identidades trigonométricas útiles se muestran en el siguiente resumen.

$\sin^2\theta + \cos^2\theta = 1$

$\tan^2\theta + 1 = \sec^2\theta$

$\cot^2\theta + 1 = \csc^2\theta$

$\sin(\alpha\pm\beta)=\sin\alpha\cos\beta\pm\sin\beta\cos\alpha$

$\cos(\alpha\pm\beta)=\cos\alpha\cos\beta\mp\sin\alpha\sin\beta$

$\tan(\alpha\pm\beta)=\frac{\tan\alpha\pm\tan\beta}{1\mp\tan\alpha\tan\beta}$

$\sin(2\theta)=2\sin\theta\cos\theta$

$\cos(2\theta)=\cos^2\theta-\sin^2\theta$

$\tan(2\theta)=\frac{2\tan\theta}{1-\tan^2\theta}$

$\sin\biggl(\frac{\theta}{2}\biggr)=\pm\sqrt{\frac{1-\cos\theta}{2}}$

$\cos\biggl(\frac{\theta}{2}\biggr)=\pm\sqrt{\frac{1+\cos\theta}{2}}$

$\tan\biggl(\frac{\theta}{2}\biggr)=\csc\theta-\cot\theta=\pm\sqrt{\frac{1-\cos\theta}{1+\cos\theta}}=\frac{\sin\theta}{1+\cos\theta}$

2. Ley de senos y cosenos

Dado el siguiente gráfico:

Leyes de senos y cosenos

Figura 2.
 Ley de senos y cosenos

La ley de senos establece que:

$\frac{a}{\sin A}=\frac{b}{\sin B}=\frac{c}{\sin C}$

La ley de cosenos establece que:

$c^2=a^2+b^2-2ab\cos c$

3. Logaritmos y exponenciales

Los logaritmos y exponenciales también hacen parte de las matemáticas operativas. Las siguientes son las leyes más representativas para el manejo de logaritmos y funciones exponenciales

$\log_a(xy)=\log_a(x)+\log_a(y)$

$\log_a(\frac{x}{y})=\log_a(x)-\log_a(y)$

$\log_a(x^r)=r\log_a(x)$

$\log_a(x)=\frac{\ln x}{\ln a}$

$a^{x+y}=a^xa^y$

$a^{x-y}=\frac{a^x}{a^y}$

$(a^x)^y=a^{xy}$

$(ab)^x=a^xb^x$

Tenga presente que $\log_ex=\ln x$ siendo $e$ el número de Euler, para mayores detalles de logaritmos y exponenciales se puede dirigir a la sección de funciones.

4. Fracciones parciales

La expansión por fracciones parciales es una técnica muy útil para simplificar un cociente de polinomios como la combinación lineal de cocientes de polinomios más simples. Pueden analizarse la expansión en fracciones parciales utilizando los siguientes casos.

4.1. Caso 1

Caso en el cual el grado del numerador sea un grado más pequeño que el denominador. El siguiente ejemplo ilustra el proceso:

$\frac{5x-4}{2x^2+x-1}$

Se pude expresar como:

$\frac{5x-4}{(x+1)(2x-1)}=\frac{A}{x+1}+\frac{B}{2x-1}$

Donde $A$ y $B$ son constantes que se puede hallar al multiplicar ambos lados de la expresión por el denominador factorizado para obtener:

$5x-4=A(2x-1)+B(x+1)$

$5x-4=(2A+B)x+(-A+B)$

Lo cual conduce a dos ecuaciones simultáneas: $5=2A+B$ y $-4=-A+B$ De allí se obtiene la solución y la expansión por fracciones parciales resulta ser:

$\frac{5x-4}{(x+1)(2x-1)}=\frac{3}{x+1}-\frac{1}{2x-1}$

4.2. Caso 2

Si el grado del numerador es mayor o igual que el denominador es necesario realizar la división de polinomios para luego aplicar las mismas consideraciones del caso 1.

4.3. Caso 3

Si el denominador tiene más de un factor lineal es necesario incluir un término correspondiente por cada factor. Por ejemplo:

$\frac{x+6}{x(x-3)(4x+5)}=\frac{A}{x}+\frac{B}{x-3}+\frac{C}{4x+5}$

Lo cual conduce a un sistema con tres incógnitas y tres ecuaciones.

4.4. Caso 4

En el caso en que el denominador posea un factor cuadrático irreducible $ax^2+bx+c$ donde el discriminante $b^2-4ac$ sea negativo entonces la fracción parcial corresponde de la siguiente manera:

$\frac{Ax+B}{ax^2+bx+c}$

5. Artículos de Interés

Colas en Java

Las colas son estructuras de datos similares a las pilas pero con un comportamiento diferente, esta también es una estructura básica puesto que se usa en muchos algoritmos y situaciones en programación.

Visita la página de listas para mayor detalle sobre esta estructura básica.

1. ¿Qué es una cola?

Una cola es una estructura de datos enlazada que sigue una disciplina FIFO (First In, First Out – Primero en entrar, Primero en salir) en la cual el primer elemento que entra es el primero en ser retirado, adicionalmente la estructura, aunque continua siendo una estructura lineal no se puede manipular cualquier elemento como sucedía con las listas, en las colas solamente se puede acceder al elemento que esta en la parte frontal, de manera que si se quiere alcanzar el último elemento, primero se requerirá desencolar todos los otros elementos. La siguiente figura muestra gráficamente una cola.

Cola enlazada
Figura 1. Colas

Las colas tienen muchos usos en informática, un ejemplo sencillo consiste en la representación de una fila de un banco o el procesamiento secuencial de una serie de actividades, por ejemplo el envío de notificaciones de correo electrónico en una aplicación transaccional.

2. Implementación de una cola

La implementación de una cola enlazada utiliza la misma clase Node que se utilizó en las listas enlazadas, la diferencia radica que en los métodos que se utilizan para manipular la cola y que se resumen a continuación:

  • add: este método se encarga de agregar un nuevo elemento en la parte de atrás (último) de la cola.
  • remove: este método remueve el elemento del frente de la cola (primer elemento) y lo devuelve.

2.1. Interface

A continuación se muestra el código correspondiente a las operaciones de una cola con su respectiva documentación.

package queues;
/**
 * Provee las acciones que se pueden realizar con una cola
 * @author ochoscar
 * @param < T > Tipo genérico de los cuales se almacenaran elementos dentro de la cola
 */
public interface IQueue< T > {
    /**
     * Método que verifica si la cola esta vacía
     * @return Devuelve true si la cola esta
     * vacía y false en caso contrario
     */
    public boolean isEmpty();
    /**
     * Método que determina el tamaño de la cola
     * @return Devuelve un entero que indica el tamaño de la cola
     */
    public int size();
    /**
     * Agrega un elemento al final de la cola
     * @param item Item de tipo genérico a ser agregado a la cola
     * @return Devuelve la misma cola que llamo al método
     */
    public IQueue add(T item);
    /**
     * Remueve un elemento del frente de la cola y lo devuelve para su manipulación
     * @return El elemento que se encontraba al frente de la cola
     */
    public T remove();
}

2.2. Clase Cola en Java

Las Colas de datos en Java se pueden implementar con la clase y consideraciones que se muestran a continuación.

La implementación de la interface anterior se encuentra en el siguiente código. Observe que se referencia tanto el primer como el último elemento a fin de realizar las operaciones de agregado y eliminación de forma óptima en cuanto a rendimiento se refiere. Algo importante también es que las operaciones de la cola no requieren de ciclos, por lo tanto la estructura se vuelve supremamente eficiente y así mismo no aporta mayor valor realizar una cola con arreglos, aunque es perfectamente posible.

package queues;
/**
 * Clase que encapsula las diferentes acciones de una cola enlazada
 * @author ochoscar
 * @param < T > Tipo genérico que contra los item de la cola
 */
public class Queue< T > implements IQueue< T > {
    ////////////////////
    // Atributos
    ////////////////////
    /** Referencia al primer nodo de la cola */
    private Node< T > front;
    /** Referencia al ultimo elemento de la cola */
    private Node< T > back;
    /** Tamaño de la cola */
    private int queueSize = 0;
    ////////////////////
    // Métodos
    ////////////////////
    /**
     * Método que verifica si la cola esta vacía
     * @return Devuelve true si la cola esta
     * vacía y false en caso contrario
     */
    @Override
    public boolean isEmpty() {
        return queueSize == 0;
    }
    /**
     * Método que determina el tamaño de la cola
     * @return Devuelve un entero que indica el tamaño de la cola
     */
    @Override
    public int size() {
        return queueSize;
    }
    /**
     * Agrega un elemento al final de la cola
     * @param item Item de tipo genérico a ser agregado a la cola
     * @return Devuelve la misma cola que llamo al método
     */
    @Override
    public IQueue add(T item) {
        Node< T > newNode = new Node(item, null);
        if(queueSize == 0) {
            front = back = newNode;
        } else {
            back.setNext(newNode);
            back = newNode;
        }
        queueSize++;
        return this;
    }
    /**
     * Remueve un elemento del frente de la cola y lo devuelve para su manipulación
     * @return El elemento que se encontraba al frente de la cola
     */
    public T remove() {
        if(queueSize == 0) return null;
        T item = front.getItem();
        front = front.getNext();
        if(front == null) back = null;
        queueSize--;
        return item;
    }
}

3. Artículos de Interés

Pilas en Java

Las pilas en Java junto con las listas constituyen una estructura básica de datos puesto que hay muchos comportamientos que obedecen al apilamiento de ítems.

Visite la sección de listas para encontrar detalles de estas estructuras de datos.

1. ¿Qué es una pila?

Una pila es una estructura de datos enlazada que sigue una disciplina LIFO (Last In, First Out – Último en entrar, Primero en salir) en la cual el ultimo elemento que entra es el primero en ser retirado, adicionalmente la estructura, aunque continua siendo una estructura lineal no se puede manipular cualquier elemento como sucedía con las listas, en las pilas solamente se puede acceder al elemento que esta en la cima, de manera que si se quiere alcanzar el elemento de la base, primero se requerirá desapilar todos los elementos. La siguiente figura muestra gráficamente una pila.

Pila enlazada
Figura 1. Pilas

Las pilas tienen muchos usos en informática, un ejemplo sencillo consiste en la pila de llamados al sistema, en la cual se apilan y desapilan los llamados a los métodos a medida que se invocan los métodos o se retorna de ellos, respectivamente.2. Implementación de una pila

La implementación de una pila enlazada utiliza la misma clase Node que se utilizó en las listas enlazadas, la diferencia radica que en los métodos que se utilizan para manipular la pila y que se resumen a continuación:

  • push: este método se encarga de agregar un nuevo elemento a la cima de la pila.
  • pop: este método remueve el elemento de la cima y lo retorna.
  • peek: este método se encarga de retornar el elemento de la cima sin eliminarlo de la pila.

1.1. Interface de las Pilas

A continuación se muestra el código correspondiente a las operaciones de una pila con su respectiva documentación.

package stacks;
/**
 * Provee las acciones que se pueden realizar con una pila
 * @author ochoscar
 * @param< T > Tipo genérico de los cuales se almacenaran elementos dentro de la pila
 */
public interface IStack< T > {
    /**
     * Método que verifica si la pila esta vacía
     * @return Devuelve true si la pila esta
     * vacía y false en caso contrario
     */
    public boolean isEmpty();
    /**
     * Método que determina el tamaño de la pila
     * @return Devuelve un entero que indica el tamaño de la pila
     */
    public int size();
    /**
     * Agrega un elemento en la cima de la pila
     * @param item Item de tipo genérico a ser agregado a la pila
     * @return Devuelve la misma pila que llamo al método
     */
    public IStack push(T item);
    /**
     * Remueve un elemento de la cima de la pila y lo devuelve para su manipulación
     * @return El elemento que se encontraba en la cima de pila
     */
    public T pop();
    /**
     * Devuelve un elemento de la cima de la pila sin removerlo
     * @return El elemento que se encontraba en la cima de pila
     */
    public T peek();
}

1.2. Clase Pila en Java

La implementación de la interface anterior se encuentra e n el siguiente código. Observe que solamente es necesario referenciar al elemento top o de la cima de la pila para realizar todas las operaciones. Algo importante también es que las operaciones de la pila no requieren de ciclos, por lo tanto la estructura se vuelve supremamente eficiente y así mismo no aporta mayor valor realizar una Pila con arreglos, aunque es perfectamente posible.

package stacks;
/**
 * Clase que encapsula las diferentes acciones de una pila enlazada
 * @author ochoscar
 * @param< T > Tipo genérico que contra los item de la pila
 */
public class Stack< T > implements IStack< T > {
    ////////////////////
    // Atributos
    ////////////////////
    /** Referencia a la cima de la pila */
    private Node< T > top;
    /** Tamaño de la cola */
    private int stackSize = 0;
    ////////////////////
    // Métodos
    ////////////////////
    /**
     * Método que verifica si la pila esta vacia
     * @return Devuelve true si la pila esta
     * vacía y false en caso contrario
     */
    @Override
    public boolean isEmpty() {
        return stackSize == 0;
    }
    /**
     * Método que determina el tamaño de la pila
     * @return Devuelve un entero que indica el tamaño de la pila
     */
    @Override
    public int size() {
        return stackSize;
    }
    /**
     * Agrega un elemento en la cima de la pila
     * @param item Item de tipo genérico a ser agregado a la pila
     * @return Devuelve la misma pila que llamo al método
     */
    @Override
    public IStack push(T item) {
        top = new Node<>(item, top);
        stackSize++;
        return this;
    }
    /**
     * Remueve un elemento de la cima de la pila y lo devuelve para su manipulación
     * @return El elemento que se encontraba en la cima de pila
     */
    @Override
    public T pop() {
        if(stackSize == 0) return null;
        T item = top.getItem();
        top = top.getNext();
        stackSize--;
        return item;
    }
    /**
     * Devuelve un elemento de la cima de la pila sin removerlo
     * @return El elemento que se encontraba en la cima de pila
     */
    @Override
    public T peek() {
        return top != null ? top.getItem() : null;
    }
}

2. Artículos de Interés

Listas Enlazadas en Java

Las listas enlazadas en Java constituyen la base de las estructuras de datos y sustentan una gran cantidad de algoritmos, en este artículo se explicaran estas listas basadas en memoria estática y memoria dinámica.

Repasa los conceptos fundamentales de Java en este vínculo.

1. Necesidad

Las listas enlazadas surgen de la necesidad de manejar de forma más eficiente la memoria, debido a que los arreglos nativos, si bien proporcionan toda la funcionalidad requerida respecto al manejo de listas tiene un gran inconveniente y es que todas sus posiciones se encuentran contiguas (juntas) en memoria, una a continuación de la otra. Suponga entonces que se crea un arreglo de gran tamaño, afortunadamente en Java cada casilla contendrá una referencia a un objeto, es decir cada casilla tiene un tamaño de 32 o 64 bits dependiendo de la arquitectura de la máquina, sin embargo, si el arreglo es muy grande es posible que no se disponga de un gran bloque de memoria contigua para albergar el arreglo.

El problema de encontrar este espacio de memoria disponible empeora con el tiempo que lleve ejecutándose el programa, puesto que la creación de objetos y su recolección ocasionan huecos en la memoria en un fenómeno conocido como fragmentación de la memoria (muy parecido al que sucede en el disco duro, conocido como fragmentación del disco). La Figura 1. muestra un ejemplo de items almacenados en casillas de un arreglo.

Arreglo tradicional
Figura 1. Arreglo tradicional

La idea tras la lista enlazada es permitir que los items que se almacenan en las casillas no tengan que estar necesariamente uno detrás del otro, y para lograr esto las listas enlazadas se forman usando el concepto de clase autoreferenciada que se muestra a continuación.

2. Clases autoreferencias

Las listas enlazadas y muchas otras estructuras de datos se forman utilizando clases autoreferenciadas que no es otra cosa que una clase que contiene al menos un atributo cuyo tipo coincide con la misma clase, en otras palabras una clase autoreferenciada tiene una referencia a un objeto del mismo tipo de la clase.

En la Figura 2, se muestra un ejemplo de una clase autoreferenciada denominada Node que representa una casilla de una lista enlazada, observe como el nodo tiene un espacio de memoria para referenciar el objeto contenido en la casilla y una referencia al nodo siguiente.

Nodo de lista enlazada
Figura 2. Nodo lista enlazada

3. Estructura de una Lista Enlazada

Las listas enlazadas aprovechan el concepto de clases autoreferenciadas para encadenar nodos y permitir armar una lista, observe que únicamente se requiere saber cual es el primer nodo de la lista, puesto que avanzando a través de la referencias next de cada nodo se puede llegar a cualquier otro nodo en la lista. El hecho de conocer la primera posición de la lista es algo que nos es familiar, ya que en el caso de los arreglos el nombre del arreglo realmente es un apuntador al primer elemento de la lista.

Observe que la lista tal como se plantea resuelve el problema de memoria mencionado anteriormente, es decir, al estar los nodos desligados los unos de los otros ya no es necesario para grandes arreglos encontrar un bloque contiguo de memoria, infortunadamente esta solución genera un nuevo inconveniente y es el desplazamiento por la lista, supongamos que se desea acceder a la casilla tercera de la lista, será necesario entonces a partir de la primera casilla iterar para posicionar una referencia que termine apuntando a la tercera casilla, esta iteración toma tiempo y empeora con el tamaño de la lista, situación que no sucede con los arreglos puesto que ubicarse en una posición cualquiera es simplemente una operación aritmética, por lo anterior indexar una posición en la lista enlazada es una operación de tiempo lineal con el tamaño de la lista, en cambio en el arreglo es una operación de tiempo constante.

El programador debe elegir adecuadamente su estructura de datos de tal manera que supla las necesidades del algoritmo, teniendo en cuenta también aspectos de rendimiento.

Lista enlazada
Figura 3. Lista enlazada

4. Implementación de Lista Enlazada

Basados en el gráfico de la Figura 3, podemos realizar la implementación de una lista enlazada, para ello consideraremos tres clases

  • Interface: especifica los métodos públicos que implementaremos para las listas.
  • Nodo: implementación de la clase autoreferenciada para representar las casillas de la lista enlazada.
  • Lista: implementación de todos los métodos y utilidades de la lista enlazada.

El código que se muestra a continuación implementa las tres clases anteriores. Note que la implementación de la interface pude darse con diversas técnicas y formas, no solo con clases autoreferenciadas, de tal manera que para la misma interface se puede tener diferentes implementaciones, la primera que se mostrará es la lista enlazada, y la segunda la lista implementada con arrays.

4.1. Interface

Esta interface representa las operaciones habituales con la lista, observe algo interesante en el caso de los métodos add, los cuales devuelven una lista enlazada, esto a primera vista puede parecer extraño, pero realmente la idea es devolver la misma lista que llamo el método add y de esta forma habilitar el encadenamiento de llamados a métodos de la lista, por ejemplo, se pueden hacer llamados así list.add(obj1).add(obj2) y asi sucesivamente.

package lists;
/**
 * Provee las acciones que se pueden realizar con una lista
 * @author ochoscar
 * @param < T > Tipo genérico de los cuales se almacenaran elementos dentro de la lista
 */
public interface IList< T > {
    /**
     * Método que verifica si la lista esta vacía
     * @return Devuelve true si la lista esta
     * vacía y false en caso contrario
     */
    public boolean isEmpty();
    /**
     * Obtiene el primer item de la lista
     * @return Devuelve la Clase al principio de la lista
     * null en caso que la lista este vacía
     */
    public T getFirst();
    /**
     * Obtiene el ultimo elemento de la lista
     * @return Devuelve la ultima Clase en la lista
     * null si la lista esta vacía
     */
    public T getLast();
    /**
     * Devuelve el i - esimo elemento de la lista
     * @param i Posición del elemento a devolver (comienza en 0)
     * @return Devuelve la Clase en la posición i
     */
    public T get(int i);
    /**
     * Método que establece un item de la lista en una posición especifica
     * @param p Objeto que sera establecido en una posición especifica
     * @param i Posición a establecer el objeto
     */
    public void set(T p, int i);
    /**
     * Método que determina el tamaño de la lista
     * @return Devuelve un entero que indica el tamaño de la lista
     */
    public int size();
    /**
     * Método que inserta al final de la lista
     * @param p Objeto a insertar
     * @return Devuelve la propia lista enlazada
     */
    public IList< T > addLast(T p);
    /**
     * Método que permite agregar un objeto en un posición
     * arbitraria de la lista
     * @param p Objeto que se quiere agregar a la lista
     * @param i posición en la cual se quiere agregar
     * @return Devuelve la propia lista enlazada
     */
    public IList< T > add(T p, int i);
    /**
     * Método que elimina un elemento dado un objeto
     * @param p Objeto a eliminar
     */
    public void remove(T p);
    /**
     * Método que remueve un elemento de la lista
     * @param i Posición o índice a eliminar de la lista
     */
    public void remove(int i);
    /**
     * Devuelve si esta o no
     * @param p Objeto a verificar si esta en la lista
     * @return Devuelve true si p esta en la lista false sino
     */
    public boolean contains(T p);
}

4.2. Nodo

La clase que establece la raíz de la auto referenciación es esta y permite además almacenar el objeto que esta contenido en esta casilla de la lista. El código proporcionado muestra que esta clase no ofrece ninguna operación particular, solamente almacena los atributos para enlazar los nodos y así conformar la lista.

package linkedlist;
/**
 * Clase auto referenciada para construir la estructura
 * tipo lista enlazada que contendrá tanto el item almacenado
 * como la referencia al siguiente elemento de la lista
 * @author ochoscar
 */
public class Node< T > {
    ////////////////////
    // Atributos
    ////////////////////
    /** Referencia al tipo T que ocupa el nodo */
    private T item;
    /** Autoreferencia al siguiente elemento */
    private Node< T > next;
    ////////////////////
    // Métodos
    ////////////////////
    /**
     * Constructor por defecto
     */
    public Node() {
    }
    /**
     * Constructor con parámetros
     * @param pItem Item a almacenar en el nodo
     * @param pNext Siguiente elemento en la lista
     */
    public Node(T pItem, Node< T > pNext) {
        item = pItem;
        next = pNext;
    }
    /**
     * Constructor de copia
     * @param pNode Objeto que servirá para realizar la copia de los
     * atributos del nodo
     */
    public Node(Node< T > pNode) {
        item = pNode.getItem();
        next = pNode.getNext();
    }
    /**
     * Método que devuelve el nodo en su
     * representación de string
     * @return Devuelve la representación en String
     */
    @Override
    public String toString() {
        return item.toString();
    }
    /**
     * @return the item
     */
    public T getItem() {
        return item;
    }
    /**
     * @param item the item to set
     */
    public void setItem(T item) {
        this.item = item;
    }
    /**
     * @return the next
     */
    public Node< T > getNext() {
        return next;
    }
    /**
     * @param next the next to set
     */
    public void setNext(Node< T > next) {
        this.next = next;
    }
}

4.3. Lista Enlazada

La clase lista implementa no solo la interface de lista sino también la interface iterator que permite devolver un objeto Iterador con el cual se recorrerá la lista, este objeto es utilizado automáticamente por java en la instrucción for - each que corresponde a la siguiente sintaxis for(Obj obj : list)

package lists;
import java.util.Iterator;
/**
 * Clase que encapsula las diferentes acciones de una lista enlazada
 * @author ochoscar
 * @param < T > Tipo genérico que contra los item de la lista
 */
public class LinkedList< T > implements IList< T >, Iterable< T > {
    ////////////////////
    // Atributos
    ////////////////////
    /** Referencia al primer nodo de la lista */
    private Node< T > first;
    /** Tamaño de la lista */
    private int listSize = 0;
    ////////////////////
    // Métodos
    ////////////////////
    /**
     * Obtiene el primer item de la lista
     * @return Devuelve la Clase al principio de la lista
     * null en caso que la lista este vacía
     */
    @Override
    public T getFirst() {
        return first != null ? first.getItem() : null;
    }
    /**
     * Obtiene el ultimo elemento de la lista
     * @return Devuelve la ultima Clase en la lista
     * null si la lista esta vacía
     */
    @Override
    public T getLast() {
        return listSize == 0 ? null : getNode(listSize - 1).getItem();
    }
    /**
     * Devuelve el i - esimo elemento de la lista
     * @param i Posición del elemento a devolver (comienza en 0)
     * @return Devuelve la Clase en la posición i
     */
    @Override
    public T get(int i) {
        if(i >= 0 && i < listSize) {
            return getNode(i).getItem();
        }
        return null;
    }
    /**
     * Método que establece un item de la lista en una posición especifica
     * @param p Objeto que sera establecido en una posición especifica
     * @param i Posición a establecer el objeto
     */
    @Override
    public void set(T p, int i) {
        if(i >= 0 && i < listSize) {
            getNode(i).setItem(p);
        }
    }
    /**
     * Método que determina el tamaño de la lista
     * @return Devuelve un entero que indica el tamaño de la lista
     */
    @Override
    public int size() {
        return listSize;
    }
    /**
     * Método que inserta al final de la lista
     * @param p Objeto a insertar
     * @return Devuelve la propia lista enlazada
     */
    @Override
    public IList< T > addLast(T p) {
        add(p, listSize);
        return this;
    }
    /**
     * Método que permite agregar un objeto en un posición
     * arbitraria de la lista
     * @param p Objeto que se quiere agregar a la lista
     * @param i posición en la cual se quiere agregar
     * @return Devuelve la propia lista enlazada
     */
    @Override
    public IList< T > add(T p, int i) {
        if(i >= 0 && i <= listSize) {
            Node< T > newNode = new Node();
            newNode.setItem(p);
            // La lista esta vacía
            if(first == null) {
                first = newNode;
            } else {
                // La lista no esta vacía, y van a inserta
                // en la posición 0
                if(i == 0) {
                    newNode.setNext(first);
                    first = newNode;
                } else if(i == listSize) {
                    // Se quiere insertar al final de la lista
                    Node< T > last = getNode(listSize - 1);
                    last.setNext(newNode);
                } else {
                    Node< T > previous = getNode(i - 1);
                    Node< T > current = previous.getNext();
                    newNode.setNext(current);
                    previous.setNext(newNode);
                }
            }
            listSize++;
        }
        return this;
    }
    /**
     * Método que elimina un elemento dado un objeto
     * @param p Objeto a eliminar
     */
    @Override
    public void remove(T p) {
        Node< T > aux = first;
        int i = 0;
        boolean find = false;
        if(contains(p)){
            while(true){
                if(aux.getItem().equals(p)){
                    break;
                }else{
                    aux = aux.getNext();
                    i++;
                }
            }
            remove(i);
        }
    }
    /**
     * Método que remueve un elemento de la lista
     * @param i Posición o índice a eliminar de la lista
     */
    @Override
    public void remove(int i) {
        if(i >= 0 && i < listSize) {
            // La posición a eliminar es valida, se procede a eliminar
            if(i == 0) {
                first = first.getNext();
            } else {
                Node< T > previous = getNode(i - 1);
                previous.setNext(previous.getNext().getNext());
            }
            // Disminuye el tamaño de la lista
            listSize--;
        }
    }
    /**
     * Devuelve si esta o no
     * @param p Objeto a verificar si esta en la lista
     * @return Devuelve true si p esta en la lista false sino
     */
    @Override
    public boolean contains(T p) {
        Node< T > iterator = first;
        while(iterator != null) {
            if(iterator.getItem().equals(p)) {
                return true;
            }
            iterator = iterator.getNext();
        }
        return false;
    }
    /**
     * Método que verifica si la lista esta vacia
     * @return Devuelve true si la lista esta
     * vacia y false en caso contrario
     */
    @Override
    public boolean isEmpty() {
        return first == null;
    }
    /**
     * Método que devuelve la lista en su
     * representación de string
     * @return Devuelve la representación en String
     */
    @Override
    public String toString() {
        String strToReturn = "[ ";
        Node< T > iterator = first;
        while(iterator != null) {
            strToReturn += iterator.toString() + " ";
            iterator = iterator.getNext();
        }
        return strToReturn + "]";
    }
    /**
     * Implementación de la interface iterable que permite recorrer la lista
     * con ciclos estilo for - each
     * @return Devuelve el iterador para recorrer la lista
     */
    @Override
    public Iterator< T > iterator() {
        // Creación de un objeto anónimo para retornarlo
        return new Iterator< T >() {
            /** Ubica un puntero en el primer elemento de la lista a retornar */
            private Node< T > iteratorNode = first;
            /**
             * Método que indica si existen mas elementos a recorrer o no
             * @return True si existen mas elementos para recorrer y false en caso contrario
             */
            public boolean hasNext() {
                return iteratorNode.getNext() != null;
            }
            /**
             * Devuelve el elemento dond se encuentra parado el iterador y lo avanza
             * @return
             */
            public T next() {
                T item = iteratorNode.getItem();
                iteratorNode = iteratorNode.getNext();
                return item;
            }
            /**
             * Método que le permite al iterador remover un elemento de forma segura
             * En la lista hay que tener cuidado cuando se remueve mientras se itera puesto
             * que la eliminación cambia el tamaño de la lista. Este método
             * no se encuentra soportado en esta version.
             */
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
    /**
     * Método que devuelve un nodo dada una posición
     * @param pos Posición de la lista para retornar el nodo
     * @return Devuelve el nodo indicado en el parámetro pos
     */
    private Node< T > getNode(int pos) {
        int i = 0;
        Node< T > iterator = first;
        while(iterator != null) {
            if(i == pos) {
                return iterator;
            }
            i++;
            iterator = iterator.getNext();
        }
        return null;
    }
}

Las listas enlazadas también se pueden disponer como listas doblemente enlazada, donde cada nodo no solamente mantenga una referencia de su siguiente elemento sino también del elemento anterior. A continuación se muestra la implementación de listas usando nodos doblemente enlazados, note que cada nodo tiene dos referencias una apuntando hacía el elemento siguiente y otra apuntando hacía el elemento anterior.

Lista doblemente enlazada
Figura 4. Lista doblemente enlazada

Algunas ventajas de esta lista es que ciertas operaciones de desplazamiento resultan más óptimas al no tener que recorrer siempre la lista completa desde el principio para posicionarse en cualquier elemento, considere en este sentido, por ejemplo, recorrer la lista en orden inverso. Otra lista importante es la Lista Circular que simplemente es aquella que se forma uniendo el principio de la lista con el final y en este caso es común utilizar un nodo ficticio para determinar un primer elemento de la lista

El programador debe elegir adecuadamente su estructura de datos de acuerdo con la naturaleza de los mismos y de esta forma se le facilitaran las operaciones, concentrarse en la lógica de fondo, realizar códigos menos propensos a errores y finalmente con mayor facilidad de mantenimiento.

5. Lista con arreglos

Como se había mencionado anteriormente la lista con arreglos puede ser una implementación de la misma interface IList. La ventaja en este caso es la facilidad para posicionarse en cualquier punto, sin embargo, al estar la lista conformada con arreglos, tiene las desventajas del uso de arreglos que se menciono al principio de esta sección. El arreglo es muy óptimo para indexare, pero cuando se acaba el tamaño es necesario crear otro nuevo con más tamaño y hacer un copiado de los elementos del arreglo original en el nuevo con mayor capacidad, esta operación ralentiza los métodos de la lista. Por esta razón las listas con arreglos deben tener mínimamente dos conceptos incluidos: el tamaño (que comúnmente se refiere al tamaño ocupado) y la capacidad (que es la máxima cantidad de elementos que puede tener el arreglo utilizado en la lista). A continuación, se presenta el código de la lista enlazada usando arreglos.

package lists;
import java.util.Iterator;
/**
 * Lista que usa arreglos nativos para implementar las operaciones de una lista
 * @author ochoscar
 * @param < T > Tipo genérico que contra los item de la lista
 */
public class ArrayList< T > implements IList< T >, Iterable< T >  {
    ////////////////////
    // Atributos
    ////////////////////
    /** Arreglo nativo de la lista que contiene los elementos y su longitud es
     la capacidad máxima de elementos a almacenar*/
    private T array[];
    /** Tamaño de la lista que refleja la cantidad de casillas del arreglo usadas */
    private int listSize;
    ////////////////////
    // Métodos
    ////////////////////
    /**
     * Constructor por defecto que crea la lista con 10 casillas de capacidad
     */
    public ArrayList() {
        array = (T[]) new Object[10];
        listSize = 0;
    }
    /**
     * Constructor especificando la capacidad inicial
     * @param initCapacity Capacidad inicial
     */
    public ArrayList(int initCapacity) {
        array = (T[]) new Object[initCapacity];
        listSize = 0;
    }
    /**
     * Método que verifica si la lista esta vacía
     * @return Devuelve true si la lista esta
     * vacía y false en caso contrario
     */
    @Override
    public boolean isEmpty() {
        return listSize == 0;
    }
    /**
     * Obtiene el primer item de la lista
     * @return Devuelve la Clase al principio de la lista
     * null en caso que la lista este vacía
     */
    @Override
    public T getFirst() {
        return listSize > 0 ? array[0] : null;
    }
    /**
     * Obtiene el ultimo elemento de la lista
     * @return Devuelve la ultima Clase en la lista
     * null si la lista esta vacía
     */
    @Override
    public T getLast() {
        return listSize > 0 ? array[listSize - 1] : null;
    }
    /**
     * Devuelve el i - esimo elemento de la lista
     * @param i Posición del elemento a devolver (comienza en 0)
     * @return Devuelve la Clase en la posición i
     */
    @Override
    public T get(int i) {
        return listSize > 0 && i > 0 && i < listSize ? array[i] : null;
    }
    /**
     * Método que establece un item de la lista en una posición especifica
     * @param p Objeto que sera establecido en una posición especifica
     * @param i Posición a establecer el objeto
     */
    @Override
    public void set(T p, int i) {
        if(listSize > 0 && i > 0 && i < listSize) {
            array[i] = p;
        }
    }
    /**
     * Método que determina el tamaño de la lista
     * @return Devuelve un entero que indica el tamaño de la lista
     */
    @Override
    public int size() {
        return listSize;
    }
    /**
     * Método que inserta al final de la lista
     * @param p Objeto a insertar
     * @return Devuelve la propia lista enlazada
     */
    @Override
    public IList< T > addLast(T p) {
        if(listSize == array.length) {
            adjustCapacity(2 * listSize);
        }
        array[listSize] = p;
        listSize++;
        return this;
    }
    /**
     * Método que permite agregar un objeto en un posición
     * arbitraria de la lista
     * @param p Objeto que se quiere agregar a la lista
     * @param i posición en la cual se quiere agregar
     * @return Devuelve la propia lista enlazada
     */
    @Override
    public IList< T > add(T p, int i) {
        if(listSize == array.length) {
            adjustCapacity(2 * listSize);
        }
        for(int j = listSize - 1; j >= i; j--) {
            array[j + 1] = array[j];
        }
        array[i] = p;
        listSize++;
        return this;
    }
    /**
     * Método que elimina un elemento dado un objeto
     * @param p Objeto a eliminar
     */
    @Override
    public void remove(T p) {
        for(int i = 0; i < listSize; i++) {
            if(array[i].equals(p)) {
                remove(i);
                break;
            }
        }
    }
    /**
     * Método que remueve un elemento de la lista
     * @param i Posición o índice a eliminar de la lista
     */
    @Override
    public void remove(int i) {
        for(int j = i; j < listSize; j++) {
            array[j] = array[j + 1];
        }
        listSize--;
    }
    /**
     * Devuelve si esta o no
     * @param p Objeto a verificar si esta en la lista
     * @return Devuelve true si p esta en la lista false sino
     */
    @Override
    public boolean contains(T p) {
        for(int i = 0; i < listSize; i++) {
            if(array[i].equals(p)) {
                return true;
            }
        }
        return false;
    }
    /**
     * Implementación de la interface iterable que permite recorrer la lista
     * con ciclos estilo for - each
     * @return Devuelve el iterador para recorrer la lista
     */
    @Override
    public Iterator< T > iterator() {
        // Creación de un objeto anónimo para retornarlo
        return new Iterator< T >() {
            /** Contador para referenciar el elemento que actualmente se itera */
            private int i = 0;
            /**
             * Método que indica si existen mas elementos a recorrer o no
             * @return True si existen mas elementos para recorrer y false en caso contrario
             */
            public boolean hasNext() {
                return i < listSize;
            }
            /**
             * Devuelve el elemento donde se encuentra parado el iterador y lo avanza
             * @return
             */
            public T next() {
                i++;
                return array[i - 1];
            }
            /**
             * Método que le permite al iterador remover un elemento de forma segura
             * En la lista hay que tener cuidado cuando se remueve mientras se itera puesto
             * que la eliminación cambia el tamaño de la lista. Este método
             * no se encuentra soportado en esta version.
             */
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
    /**
     * Método privado utilitario encargado de asegurar una capacidad en el arreglo
     * haciendo una copia de los elementos del arreglo viejo en el nuevo
     * @param newCapacity Nueva capacidad del arreglo
     */
    private void adjustCapacity(int newCapacity) {
        T newArray[] = (T[]) new Object[newCapacity];
        for(int i = 0; i < listSize; i++) {
            newArray[i] = array[i];
            array[i] = null;
        }
        array = newArray;
    }
}

La siguiente imagen muestra el diagrama correspondiente a la lista implementada con arreglos.

Lista implementada con arreglos
Figura 5. Lista implementada con arreglos

Observe que el tamaño del arreglo, es decir, su capacidad puede verse incrementada en la operación add, en la cual se puede duplicar el tamaño del arreglo; una practica común es reducir también el tamaño del arreglo cuando se remueve, y es común disminuir el tamaño del arreglo a un cuarto del mismo si la mitad del arreglo esta libre. Es importante con lo anterior evitar que se hagan secuencias de operaciones que dupliquen – recorren el tamaño del arreglo, es decir add – remove – add – remove y así sucesivamente decrementando sustancialmente el rendimiento del arreglo.

6. Artículos de Interés

Ejecución de programas en Java

Según el tipo de aplicación, por ejemplo web, escritorio o móvil se debe seguir una serie de pasos para ejecutar un programa, en este artículo se aborda la ejecución de programas en Java de escritorio desde terminal o consola.

1. Ejecución de programas Java

1.1. Verificación de instalación de Java

Cuando pensamos en ejecutar programas Java para el escritorio requerimos hacerlo desde una consola o terminal, es importante para el ambiente cuente con el comando java instalado y sea accesible desde el lugar o directorio donde se ejecute el comando. En la figura 1 se muestra un ejemplo de ejecución del comando java -version que muestra la versión de la máquina virtual de Java que ejecutará el comando.

Es importante reconocer que también existen otros comandos para compilar como javac o algunas utilidades para revisar la carga de memoria o el comportamiento del procesador y que estas utilidades están disponibles en la carpeta bin de la instalación.

Para este punto lo más normal es compilar y ejecutar los archivos java utilizando un Entorno Integrado de Desarrollo o IDE como Eclipse, NetBeans o IntelliJ.

Verificación de instalación de Java
Figura 1. Ejecución del comando Java

1.2. Ejecución de programa en Java desde la consola

Ahora, una vez que el comando java se encuentra disponible se podrán ejecutar programas java simplemente pasando el archivo .class como parámetro de Java y teniendo presente la jerarquía de paquetes donde se almaceno nuestra clase, por ejemplo la figura 2 muestra la ejecución del programa HolaMundo mostrado en el listado de código 1.

Ejecutar programa con Java
Figura 2. Ejecución de un programa Java
public class HolaMundo {
    public static void main(String args[]) {
        System.out.println("Hola mundo");
    }
}

Aquí se puede comprender el método main que es el punto de entrada para la ejecución de instrucciones en Java y el mismo debe ser estático para que la JVM pueda llamar el método sin necesidad de instanciar la clase HolaMundo, también debe ser público para que la JVM pueda ver el método, debe llamarse main específicamente puesto que con este nombre lo buscará la JVM.

Finalmente pueden pasarse una serie de parámetros a manera de arreglo e String, estos parámetros son enviados en el momento de la ejecución en la línea de comandos, y simplemente mencionar que si se envían parámetros que contengan espacios y estos van a ser interpretados como un solo ítem en el arreglo args deberá escribirse entre comillas dobles.

Para mayor información de clases métodos y atributos de Click aquí.

Alternativamente se pueden ejecutar los programa java si el proyecto es empaquetado como un jar, es decir, como un archivo comprimido java el cual contiene un conjunto de archivos .class y un archivo manifiesto que indica cual de las clases es la principal y que a su vez contiene un método main que será utilizado para iniciar la ejecución de instrucciones. El comando necesario para ejecutar un jar es el siguiente: java -jar ArchivoJAVAJAR.jar. Si el archivo jar no contiene un manifiesto o la clase principal no contiene un main con las consideraciones descritas nos será posible ejecutarlo.

2. Artículos de Interés

Encapsulamiento en Java

Si bien el encapsulamiento es uno de los pilares de la programación orientada a objetos en este artículo se expone las consideraciones especificas para el lenguaje Java.

1. Encapsulamiento

El encapsulamiento en java y es uno de los pilares de la programación orientada a objetos que establece la posibilidad de ocultar la información de las propiedades de un objeto de forma que las mismas no se accedan directamente.

Hasta este momento ya se conoce acerca de la forma de escribir clases y crear objetos, además se ha vinculado la sintaxis de la programación estructurada en el sentido que se cuenta con estructuras para controlar el flujo de un algoritmo, ahora entenderemos como las propiedades (recuerde que también se llaman atributos o campos) pueden ser ocultados para que los objetos no puedan modificarlos directamente.

1.1. Ocultamiento de la información

El objetivo entonces será ocultar la información de la implementación de forma que los usuarios programadores que utilicen la clase no puedan acceder a estos detalles, lo anterior mejorará la abstracción permitiendo que los programadores se enfoquen en usar atributos y métodos seguros con los cuales no puedan generar inadvertidamente daños.

2. Ejemplos

Para comprender estos conceptos consideremos la clase Tiempo.

class Tiempo {
    //////////////////////////
    // Atributos
    //////////////////////////
    private int hora;
    private int minutos;
    //////////////////////////
    // Métodos
    //////////////////////////
    public int getHora() {
        return hora;
    }
    public void setHora(int hora) {
        hora = this.hora;
    }
    public int getMinutos() {
        return minutos;
    }
    public void setHora(int minutos) {
        minutos = this.minutos();
    }
}

En esta clase se puede apreciar como los atributos son privados, lo cual indica que los objetos que creemos de dicha clase no podrán acceder directamente a los atributos, estos objetos aunque si contienen los atributos (hora y minutos) no podrán utilizarlos ya que al ser privados solamente se podrán usar en el mismo archivo y más concretamente en la misma clase donde fueron declarados.

Al parecer no existiría ninguna ventaja al tener los atributos privados puesto que sería mejor tenerlos públicos y evitarse el paso por un método para establecerlo (set) y obtenerlo (get) compare las siguientes dos líneas de código que muestran estas dos posibilidades.

Tiempo t = new Tiempo();
t.setHora(20);
t.hora = 40;

2.2. Comparación privado y público

El primer set de la variable Hora se realiza con el atributo encapsulado y privado mientras que el segundo se realiza con el atributo público, observe que en el segundo caso no existe ninguna forma de evitar asignar un valor inválido tal como esta sucediendo con el valor 40, de esta forma el encapsulamiento ayuda a evitar que los objetos tengan valores inconsistentes.

3. Utilidad del encapsulamiento

Algo adicional que debe conservar en mente y que es algo esencial del encapsulamiento es el hecho de que aísla la representación interna del objeto de su interface, en palabras más sencillas la representación interna dada por los atributos, sus valores y estados están tras una capa de métodos getter y setter, de forma que en una aplicación podría pensar en cambiar la forma como se guarda internamente las horas y decidir.

Por ejemplo, guardarlo con un solo entero, este cambio mejoraría el desempeño de la memoria afectando el desempeño en tiempos, sin embargo quienes hayan usado las clase Tiempo no se verán afectados y seguirán utilizando los mismos métodos getter y setter sin importar que internamente se guarden dos enteros para representar la hora y los minutos o un solo atributo para guardar, por ejemplo la cantidad de minutos que hayan pasado desde la media noche.

Repasa los fundamentos de las clases, métodos y atributos de java.

3. Artículos de Interés

Control de Flujo en Java

Cuando se desarrollan algoritmos se debe considerar tanto las estructuras de datos como los controles de flujo cuya sintaxis en Java se recopila en este artículo.

1. Control de flujo: instrucciones condicionales

En Java las instrucciones de la programación estructurada están disponibles para controlar el flujo y determinar que instrucciones ejecutar o cuantas veces repetirlas.

Visita la página de historia de java para más detalles introductorios.

La primera instrucción de control es el condicional cuya forma compuesta se muestra a continuación.

// Forma simple del condicional
if(expresión booleana) {
    // Bloque en caso que la expresión sea verdadera
}
// Forma compuesta del condicional
if(expresión booleana) {
    // Bloque en caso que la expresión sea verdadera
} else {
    // Bloque en caso que la expresión sea verdadera
}
// Condicional en escalera
if(expresión booleana 1) {
    // Bloque en caso que la expresión 1 sea verdadera
} else if(expresión booleana 2) {
    // Bloque en caso que la expresión 2 sea verdadera
} else if(expresión booleana 3) {
    // Bloque en caso que la expresión 3 sea verdadera
} else{
    // Bloque en caso que ninguna expresion sea verdadera
}

Los puntos clave a resaltar sobre la instrucción condicional son los siguiente:

  • Los paréntesis en la expresión booleana son obligatorios.
  • No es requerido el uso de la parte del else en el código.
  • Es supremamente importante tener en cuenta que el código dentro de un bloque debe tener sangría aplicada.
  • En la tercera forma del condicional (en escalera) aunque varias expresiones booleanas sean verdaderas solamente se ejecuta el bloque correspondiente a la primera expresión booleana verdadera, así mismo puede haber un número indefinido de segmentos else if.
  • Tenga presente que en muchas ocasiones el operador ternario simplifica la escritura del código, sin embargo, debe balancear dicha simplicidad con la dificultad de leer expresiones muy crípticas.
  • En caso que los bloques tengan una sola instrucción no es necesario usar llaves, no obstante lo anterior es muy recomendado por legibilidad siempre usar llaves.

1.1. Instrucción swtich

Otra instrucción condicional es switch cuya sintaxis se puede apreciar en el siguiente segmento de código.

switch(variable) {
    case CONSTANTE1:
        // código 1
        break;
    case CONSTATE2:
        // código 2
        break;
    default:
        // código por defecto
}

En el caso de la instrucción switch se puede apreciar que la misma es utilizada de forma similar a un if en escalera, estos permiten realizar el control de flujo las bifuraciones del código . Hay que recordar que las instrucciones que se ejecutaran corresponden a la primera comparación verdadera de la variable con las diferentes constantes, igualmente solo se ejecuta un bloque de código que esta marcado desde los dos puntos y hasta el siguiente break, en ocasiones y dado que la instrucción break es opcional, se pueden juntar varios segmentos case para ejecutar un mismo código para cualquiera de las constantes que acompañen dichos segmentos case.

Note también que hay un segmento por defecto y es opcional, este se ejecuta cuando ninguna de las comparaciones con las constantes arrojo un resultado verdadero. Los tipos de datos soportados para la variable son: int, Integer, byte, Byte, short, Short, char, Character, String, y enum.

De otra parte las constantes, deben ser o bien literales o variables marcadas con la palabra reservada final, el tipo de dato de las constantes debe coincidir con el tipo de dato de la variable, además observe que final hace que la variable se convierta en constante y únicamente pueda recibir valor en el momento de su creación, sin embargo el código no compilara si usa una variable como constante y la misma es un parámetro de una función, así esta última este marcada como final.

2. Control de flujo: ciclos

Los ciclos son otra forma de realizar control de flujo en java al repetir o iterar una serie de isntrucciones.

2.1. Ciclo while

El ciclo más sencillo y fácil de utilizar es el while, el cual repite las instrucciones mientras una condición sea verdadera, en este sentido observe que en sintaxis se parece mucho al condicional if. A continuación se presenta su sintaxis.

while(condición booleana) {
    // Código
}

2.2. Ciclo do while

De forma similar, se encuentra el ciclo do - while el cual es similar al anterior, excepto que este se repite al menos una vez ya que la condición se evalúa al final del ciclo y no al principio. Su sintaxis se muestra a continuación.

do {
    // Código
} while(condición booleana);

2.3. Ciclo for

El tercer ciclo y de hecho uno de los más comunes es el ciclo for el cual en su presentación básica se muestra a continuación

for(inicialización, expresión booleana, actualización) {
    // Código
}

Las tres partes del ciclo for son opcionales y si todas se omiten el ciclo sería un ciclo infinito. También es posible separar múltiples sentencias de inicialización o actualización utilizando comas.

Es importante recordar que la inicialización no puede realizar ocultamiento de variables, es decir, si la variable declarada en la inicialización ya fue declarada antes del ciclo for el compilador arrojará un error indicando que se tiene una doble declaración de la variable, de esta manera las variables declaradas en la inicialización pertenecerán al ámbito del cuerpo del ciclo y no se podrán usar por fuera de este. El último ciclo disponible en Java es el ciclo for - each el cual se muestra a continuación.

for(TipoDato variable : colección) {
    // Código
}

En este caso la colección debe ser una implementación de java.lang.Iterable y el ciclo se ejecuta una vez por cada ítem de la colección, asignando en cada repetición un valor de la colección.

Algo importante a tener en cuenta con los diferentes ciclos es que los bloques de código pueden omitir las llaves cuando solo se trate de una sola línea, en estos casos es importante mantener la tabulación o poner la línea de código al frente de la estructura de control. En cualquier caso esta práctica no es muy recomendable puesto que es más propensa a introducir errores en el código.

3. Etiquetas, continue y break

Las sentencias (if, while, do while, for, switch) de Java pueden ser iniciadas con una etiqueta, normalmente escrita en mayúsculas y terminada en dos puntos. Estas etiquetas sirven para marcar los puntos donde se puede devolver el código mediante una sentencia break o continue, estas dos instrucciones permiten o romper un ciclo con la finalidad de salirse del mismo y no continuar su ejecución o en el caso de continue continuar de inmediato con la siguiente iteración del ciclo sin importar que falten instrucciones por ejecutar. La sintaxis de estas etiquetas es:

break etiquetaOpcional;
continue etiquetaOpcional;

Estas instrucciones están disponibles para los ciclos, adicionalmente las sentencia switch soporta el uso de break. El uso de estas instrucciones es particularmente útil cuando en un ciclo se detecta que no se requiere seguir ejecutando, por ejemplo, si se esta realizando una búsqueda y se encuentra el ítem deseado no se requeriría seguir iterando para buscar, en este caso se puede recurrir a la instrucción break o si la iteración ya no se requiere hacer se usaría continue.. Un ejemplo del uso de etiquetas junto con una sentencia break y continue se muestra a continuación.

PRIMERO: for( ... ) {
    SEGUNDO: for( ... ) {
        if( ... ) {
            // Rompe el ciclo actual terminándolo y haciendo
            // un salto a la etiqueta PRIMERO
            break PRIMERO;
        }
        if( ... ) {
            // Deja de ejecutar la iteración actual del ciclo
            // interno y brinca a la etiqueta SEGUNDO,
            // en este caso no es necesario el uso de etiquetas
            // puesto que la instrucción continue esta en el
            // cuerpo del segundo ciclo
            continue SEGUNDO;
        }
        // ... código ...
    }
}

4. Artículos de Interés

« Entradas anteriores Entradas recientes »

© 2024 ochoscar's blog

Tema por Anders NorenArriba ↑