Saltar al contenido principal
LibreTexts Español

5.1: Arreglos de punto flotante de índice único

  • Page ID
    87488
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    \( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)

    ( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\id}{\mathrm{id}}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\kernel}{\mathrm{null}\,}\)

    \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\)

    \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\)

    \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    \( \newcommand{\vectorA}[1]{\vec{#1}}      % arrow\)

    \( \newcommand{\vectorAt}[1]{\vec{\text{#1}}}      % arrow\)

    \( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vectorC}[1]{\textbf{#1}} \)

    \( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)

    \( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)

    \( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)

    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    Concepto

    A menudo ocurre que tenemos un conjunto ordenado de, digamos\(n\), “elementos” de datos que de alguna manera están relacionados. El índice podría representar direcciones en tres dimensiones de espacio\((k=1,2,3\) para\(x, y, z\), respectivamente) - en el que almacenamos, en cada ubicación de matriz, la coordenada correspondiente de un punto (una matriz de longitud 3); o el índice podría representar 15 tiempos diferentes - en los que almacenamos, en cada ubicación, el tiempo de la medición, o tal vez la medición en sí (una matriz de longitud 15). Tenga en cuenta que el índice juega el papel de variable independiente y el valor de matriz juega el papel de variable dependiente. En todos estos casos a menudo desearemos operar en todos los\(n\) “elementos relacionados” de manera similar. Por lo tanto, nos gustaría una manera de hacer referencia a todos los elementos con un nombre común, y una manera fácil de hacer referencia a diferentes elementos a través de este nombre común, con el fin de desarrollar código sucinto, legible y eficiente para implementar operaciones comunes en todos los elementos. En particular, no quisiéramos escribir\(n\) líneas de código cada vez que quisiéramos, digamos, cuadrar cada uno de los\(n\) elementos.

    Una matriz de índice único, en esta sección, una matriz de índice único de punto flotante, es la “clase” más simple que logra estos objetivos. Primero introducimos un nombre de variable, array_name, que se asociará a todos los\(n\) elementos de la matriz. Luego indexamos este array_name para acceder a cualquier elemento particular de la matriz: en particular, array_name (i) es el puntero al elemento\(i\) de la matriz. Destacamos que, como en el caso escalar, array_name (i) no es el valor del elemento i de la matriz sino la ubicación en la memoria en la que almacenaremos el valor del elemento\(i\). Por ejemplo, array_name\((2)=3.14159\) asignaría el valor\(3.14159\) al segundo elemento de la matriz. (A continuación discutimos métodos de asignación más eficientes).

    Conceptualmente, puede pensar en la matriz como almacenada en ubicaciones\(n\) contiguas en la memoria. El elemento 1 de la matriz se almacena en array_name (1), el elemento 2 de la matriz se almacena en array_name (2)\(\ldots\), y el elemento\(n\) de la matriz se almacena en array_name\((\mathrm{n})\). En este sentido (virtual), basta con (decir) pasar a una función simplemente el nombre de la matriz variable, que puede ver como la dirección del primer elemento, ya que las direcciones de todos los demás elementos de la matriz se pueden deducir fácilmente del nombre de la matriz. (En la práctica real, también hay cierta información de encabezado asociada con la matriz, por ejemplo, en nuestro caso de índice único, la longitud)\(n\). Dado que muchas operaciones de matriz comunes se pueden realizar en MATLAB con llamadas a funciones simples, o llamadas a funciones definidas por el usuario, a un alto nivel, a menudo podemos tratar exclusivamente con array_name sin referencia explícita a índices particulares. (Por supuesto, bajo el capó...)

    Asignación y Acceso

    La forma más explícita de crear una matriz de índice único es a mano:\(X=[1,3,4]\) crea una matriz de índice único de longitud 3, con entradas\(X(1)=1, X(2)=3\), y\(X(3)=4\). Para determinar la longitud de una matriz MATLAB proporciona una longitud de función. En nuestro ejemplo

    >> X = [1,3,4]
    X =
        1     3     4
    >> X(1) 
        ans = 1
    >> length(X)
    ans =
        3
    >>
    

    Tenga en cuenta que esta matriz de índice único es una matriz de índice único de fila. (También podemos considerar matrices de índice único de columna, en las que las comas anteriores son reemplazadas por punto y coma:\(\mathrm{X}=[1 ; 3 ; 4]\). Reservamos el tratamiento de filas y columnas a nuestra discusión de matrices de doble índice.)

    El proceso de entrada es facilitado por el operador de colon. En particular,\(\mathrm{Z}=[\mathrm{J}: \mathrm{D}: \mathrm{K}]\) crea la matriz de índice único\(J, J+D, \ldots, J+m * D\)] for\(m=f i x((K-J) / D)\), donde\(f i x\) es una función MATLAB que redondea al entero más cercano hacia cero. (Tenga en cuenta que\(\mathrm{J}: \mathrm{D}: \mathrm{K}\) está vacío si\(\mathrm{D}==0\), si\(D>0 \& J>K\), o si\(D<0\) &\(J<K\).) El valor predeterminado de\(D\) es 1 y por lo tanto\(J: K\) es equivalente a\(\mathrm{J}: 1: \mathrm{K}\), como en el ejemplo

    >> firstfive = [1:5]
    firstfive =
        1     2     3     4     5
    >>
    

    De hecho, reconocerá esta construcción de colon a partir de la declaración for; y de hecho la declaración for puede tomar la forma para VARCOUNTER\(=\mathrm{S}\) donde\(\mathrm{S}\) está cualquier matriz de índice único; revitiremos esta construcción de la declaración for en breve.

    Podemos asignar una matriz completa,

    >> Y = X
    Y =    
        1     3     4
    >>
    

    o podemos asignar o reasignar un elemento en particular como, por ejemplo,

    >> X(3) = 10;
    >> X
    X =
        1     3     10
    >>
    

    que vemos modifica en\(X\) consecuencia.

    Por supuesto, el punto de una matriz es que tenemos un enfoque numérico sistemático para la indexación, y por lo tanto podemos asignar fácilmente valores con una declaración for. Presentamos dos enfoques. En la primera, salimos a cero para inicializar:

    >> Z = zeros(1,10);
    >> for i = 1:length(Z)
        Z(i) = i^2;
       end
    >> Z
    Z =
        1     4     9     16     25     36     49     64     81     100
    >>
    

    Tenga en cuenta que ceros\((1, n)\) es una función MATLAB que proporciona una matriz de índice único (fila) de todos los ceros de longitud\(n\). (Tenga en cuenta que el primer argumento de ceros indica que deseamos formar una matriz de índice único de fila; ceros\((n, 1)\) crearían una matriz de índice único de columna de todos los ceros. Entenderemos mejor los ceros una vez que discutamos las matrices de índices múltiples.) Este enfoque, de inicialización, se prefiere siempre que sea posible: permite que MATLAB sea más eficiente en la asignación y administración de memoria.

    En el segundo enfoque, concatenamos:

    >> Z = [];
    >> for i = 1:10
        Z = [Z,i^2];
       end
    >> Z  
    Z =
        1     4     9     16     25     36     49     64     81     100
    >>
    

    Aquí\(Z=[]\) define una matriz pero la matriz está inicialmente vacía: hacemos esto porque no podemos “agregar a” (anexar a, o concatenar a) una matriz que no existe. Tenga en cuenta que la expresión [Z,\(\left.i^{\wedge} 2\right]\) se evalúa como una matriz en la que los primeros elementos length (\(Z\)) son los elementos de\(Z\) y el último elemento es\(i^{\wedge} 2\). Así, en cada iteración la longitud de\(\mathrm{Z}\) crece. Lo anterior es menos eficiente que el enfoque de inicialización, pero muy conveniente en particular (por ejemplo, en una sentencia while) cuando no conocemos a priori el número de elementos en nuestro array (ultimate).

    Como nuestro último punto sobre asignación y acceso, observamos que MATLAB admite una forma muy conveniente de direccionamiento indirecto. En particular, si creamos una matriz de índice único de enteros indvec entonces podemos extraer de (digamos) Z solo aquellos elementos con índices en indvec:

    >> indvec = [1,3,5,9];
    >> U = Z(indvec)
    U =
        1     9     25     81
    >>
    

    Tenga en cuenta que también puede aplicar direcciones indirectas en el lado izquierdo de la instrucción de asignación, pero se debe tener cierto cuidado con respecto a la “forma” (fila frente a columna) de la matriz de índice único resultante.

    Tenga en cuenta que en todos estos atajos siempre hay un programa subyacente equivalente (que es más o menos cómo se implementan estos atajos). Por ejemplo, en lo anterior, un argumento de índice de matriz le dice a Matlab que ejecute, efectivamente:

    >> U_too = zeros(1,length(indvec))
    >> for inew = 1:length(indvec)
            U_too(inew) = Z(indvec(inew));
       end
    >> U_too
    U_too =
        1     9     25     81
    >>
    

    Pero claro es mucho mejor encapsular y reutilizar esta característica dentro de la sintaxis de Matlab que reescribir este pequeño bucle en cada ocasión.

    Operaciones aritméticas (punteadas)

    Es una de las principales ventajas de los lenguajes de programación que a medida que desarrollamos estructuras de datos (o tipos, o clases) más convenientes, en particular con muchos elementos, también podemos definir adecuadamente nuestras operaciones para tratar directamente con estas nuevas entidades, en su conjunto, en lugar de manipular explícitamente cada elemento . En esta sección definimos para el caso de matrices de índice único los operadores aritméticos de matriz correspondientes.

    Primero discutimos las operaciones elemento por elemento. En estas operaciones, consideramos dos matrices de la misma longitud y aplicamos la misma operación aritmética en cada par de elementos que comparten el mismo índice. Comenzamos con la sución/resta (y la multiplicación por un escalar):

    >> P = [1, 4, 7]; Q = [2, -1, 1];
    >> R = P + 3.0*Q
    R =
        7     1     10
    >>
    

    que es simplemente una abreviatura para el bucle

    >> for i = 1:length(P)
            R_too(i) = P(i) + 3.0*Q(i);
       end
    >> R_too
    R_too =
        7     1     10
    >>
    

    El bucle deja en claro la interpretación de “elemento por elemento”, pero obviamente la declaración de una línea es muy preferida.

    Note no\(3.0\) es una matriz, es escalar, que escala todos los elementos de la matriz Q en nuestro ejemplo anterior. Esta característica de escalado simple puede ser muy conveniente para definir matrices de puntos de cuadrícula o puntos de datos (por ejemplo, en interpolación, diferenciación e integración). A saber, si tenemos un intervalo de longitud\(L\) y deseamos crear\(N\) segmentos de longitud\(L / N\), solo necesitamos hacer\(>>\) xpts\(=(\mathrm{L} / \mathrm{N}) *[0: \mathrm{N}]\). Tenga en cuenta que xpts es de longitud (en el sentido de número de elementos en la matriz)\(\mathrm{N}+1\) ya que incluyen ambos puntos finales:\(\operatorname{xpts}(1)=0\) y\(\operatorname{xpts}(\mathrm{N}+1)=\mathrm{L} / \mathrm{N}\).

    Finalmente, observamos un atajo adicional: si\(q\) es un escalar, entonces la adición elemento por elemento a nuestro vector\(P\) se agregará\(q\) a cada elemento de\(P\). A saber,

    >> q = 3;
    >> P + q
    ans =
        75     4     7     10
    >>
    

    Podríamos escribir lo anterior más correctamente como

    >> P + q*ones(1,length(P))
    ans =
        4     7     10
    >>
    

    pero no hay necesidad dada la expansión automática de q de MATLAB cuando se encuentra en la adición de matrices. Note ones\((1, n)\) es una función MATLAB que crea una matriz de índice único (fila) de longitud\(n\) con todos los elementos establecidos en unity.

    Para implementar la multiplicación, división y exponenciación elemento por elemento hacemos

    >> PdotmultQ = P.*Q
    PdotmultQ =
        2     -4     7
    >> PdotdivideQ = P./Q
    PdotdivideQ =
        0.5000     -4.0000     7.0000
    >> PdotexpQ = P.^Q
    PdotexpQ =
        1.0000     0.2500     7.0000
    >>
    

    que es equivalente a

    >> for i = 1:length(P)
        PdotmultQ_too(i) = P(i)*Q(i);
        PdotdivideQ_too(i) = P(i)/Q(i);
        PdotexpQ_too(i) = P(i)^Q(i);
       end
    >> PdotmultQ_too
    PdotmultQ_too =
        2     -4     7
    >> PdotdivideQ_too
    PdotdivideQ_too =
        0.5000     -4.0000     7.0000
    >> PdotexpQ_too
    PdotexpQ_too =
        1.0000     0.2500     7.0000
    >>
    

    En cuanto a la suma, si reemplazamos uno de nuestros vectores por un escalar, Matlab se expandirá con un vector “ones”.

    ¿Por qué necesitamos el “punto” antes de los operadores\(*, /\), y ^ - los llamados operadores “punteados” (o elemento-byelement): multiplicación punteada, división punteada y exponenciación punteada? Resulta que hay dos tipos de entidades que se ven muy similares, respectivamente arrays y vectores (luego matrices multi-índice y matrices): ambas son conjuntos ordenados de números de\(n\) coma flotante. Sin embargo, las operaciones aritméticas se definen de manera muy diferente para estas dos entidades, respectivamente, operaciones de elementos para matrices y operaciones algebraicas lineales (por ejemplo, productos internos) para vectores. Podríamos definir fácilmente decir\(*\) para realizar la multiplicación elemento por elemento para objetos que se definen como matrices, y realizar un producto interno para objetos definidos como vectores. Desafortunadamente, aunque conceptualmente bastante limpio, esto sería muy engorroso ya que a menudo en una línea deseamos tratar un conjunto de números como una matriz y en la siguiente línea deseamos tratar el mismo conjunto de números como un vector: habría mucha conversión y teneduría de libros. Por lo tanto, MATLAB prefiere una especie de “superclase” de entidades de matriz y vector (y matriz), y por lo tanto obliga a alguna nueva sintaxis para indicar si queremos tratar la matriz y el vector como una matriz (con operadores punteados elemento por elemento) o un vector (con operadores de álgebra lineal sin puntos). Tenga en cuenta que no requerimos punteado\(+\) (o punteado -) ya que la adición elemento por elemento y la adición de vectores de hecho son equivalentes. Entonces ahí.

    Observamos que hay muchas operaciones aritméticas en matrices además de operaciones elemento por elemento, muchas de las cuales están disponibles como funciones de Matlab. Por ejemplo, podemos realizar fácilmente la suma de los tres primeros enteros (como hicimos anteriormente con un bucle for) como

    >> ints = [1:3];
    >> sum(ints)
    ans =
        6
    >> mean(ints)
    ans =
        2
    >>
    

    donde sum realiza la suma de todos los elementos en una matriz (aquí ints) y mean calcula la media aritmética de todos los elementos en una matriz.

    Por último, observamos aquí que los muchos Matlab bui b9">sin y cos, pero hay muchos más —buscan “función matemática” y “funciones especiales” en el documento— que también aceptan argumentos de índice único (y de hecho, doble—índice). Por ejemplo,

    >> xpts = (pi/4)*0.5*[0:2];
    >> sin_values = sin(xpts)
    sin_values =
        0     0.3827     0.7071
    >>
    

    con una sola llamada proporciona los valores de pecado para todos los elementos de xpts.

    Operaciones Relacionales y Lógicas (Array)

    Para las operaciones relacionales y lógicas no tenemos la complicación de la combinación matriz/vector y por lo tanto no necesitamos puntos. En efecto, cuando aplicamos cualquiera de nuestras operaciones relativas/lógicas escalares a pares de vectores de la misma longitud, Matlab devuelve un vector (de la misma longitud que los operandos) que es el resultado de la operación relativa/lógica escalar elemento por elemento.

    Como ejemplo,

    >> x = [1.2, 3.3, 2.2]; y = [-0.1, 15.6, 2.0];
    >> z_1 = (x < y)
    z_1 =
        0     1     0
    >> z_2 = (x > y)
    z_2 =
        1     0     1
    >> z_3 = z_1 | ~z_2
    z_3 =
        0     1     0 
    >>
    

    Ejemplo similar se puede construir para todos nuestros operadores relacionales y operadores lógicos. Tenga en cuenta que\(z_{-} 1, z_{-} 2\) y\(z_{-} 3\) son matrices lógicas: cada elemento es una instancia del tipo de datos lógicos.

    Para completar, indicamos la implementación de lo anterior como un bucle for:

    >> for i = 1:length(x)
        z_1_too(i) = (x(i) < y(i));
        z_2_too(i) = (x(i) > y(i));
        z_3_too(i) = z_1_too(i) | ~ z_2_too(i) ;
       end
    >> z_1_too
    z_1_too =
        0     1     0
    >> z_2_too
    z_2_too =
        1     0     1
    >> z_3_too
    z_3_too =
        0     1     0
    >>
    

    que en efecto es equivalente, pero tedioso.

    Operaciones de “Datos”

    También hay una serie de operaciones que aunque numéricas se centran tanto en los índices como en los datos, y por lo tanto las incluimos bajo el rubro operaciones de “datos”.

    Varias funciones de Matlab están disponibles para reordenar los elementos o identificar elementos distinguidos: sort, min y max son quizás los más útiles en este sentido. Ilustramos solo una de estas funciones, min:

    >> T = [4.5, -2.2, 6.3, 4.4];
    >> [minimum, minimizer] = min(T)
    minimum =
        -2.2000  
    minimizer =
        2
    >> minimum_too = min(T)
    minimum_too =
        -2.2000
    >>
    

    lo que arroja el resultado obvio. Este es nuestro primer ejemplo de una función con dos salidas: mínimo es el mínimo de la matriz, y minimizador es el índice del elemento minimizador. Tenga en cuenta también que si solo deseamos obtener la primera salida podemos abreviar la llamada.

    Quizás una de las funciones de datos de matriz más útiles es la función find. Dado un vector lógico L, find (L) devolverá un vector que contiene (en orden creciente) los índices de todos los elementos de L que son distintos de cero (y por lo tanto son unidad, ya que L es una matriz lógica). (De hecho, find también se puede aplicar a una matriz doble, pero hay que tener cuidado con los efectos de redondeo). Como ejemplo:

    >> L = logical([0,1,1,0,0,1]);
    >> islogical(L)
    ans =
        1
    >> ind_of_nonzero = find(L)
    ind_of_nonzero =
        2     3     6
    >>
    

    donde también hemos ilustrado la construcción de un vector lógico. Tenga en cuenta que la función find implementa efectivamente el bucle for

    >> ind_of_nonzero_too = [];
    >> for i = 1:length(L)
       if( L(i) ~= 0 )
            ind_of_nonzero_too = [ind_of_nonzero_too,i];
       end
    end
    >> ind_of_nonzero_too
    ind_of_nonzero_too =
        2     3     6      
    >>
    

    lo que demuestra también una aplicación de concatenación.

    La función find es muy útil en el contexto de las comparaciones. Por ejemplo, es posible que deseemos extraer solo esos valores de vector mayores que algún umbral:

    >> H = [0.2, 1.4, 6.7, -3.4, 4.2];
    >> log_H_thresh = (H > 1.2)
    log_H_thresh =
        0     1     1     0     1
    >> inds_H_thresh = find(log_H_thresh)
    inds_H_thresh =
        2     3     5
    >> H(inds_H_thresh)
    ans =
        1.4000     6.7000     4.2000
    >>
    

    Por supuesto, podemos sustituir\(\mathrm{H}>1.2\) en lo anterior con cualquier composición más complicada de operadores relacionales y lógicos.

    Por supuesto, podríamos combinar esto como

    >> H ( find ( H > 1.2 ) )
    ans =
        1.4000     6.7000     4.2000
    >>
    

    De hecho, Matlab acepta una abreviatura adicional como

    >> H ( H > 1.2 )
    ans =
        1.4000     6.7000     4.2000
    >>
    

    en el que un hallazgo se aplica automáticamente a un vector de índice lógico. Al parecer, este enfoque un tanto sintácticamente descuidado es de hecho el más eficiente.

    Aprovechamos esta oportunidad para volver a visitar el para declaración. Inicialmente introdujimos la declaración for del formulario para VARCOUNTER = LIM_1:INC:LIM_2 y luego notamos que la sentencia for puede tomar una forma más general para VARCOUNTER = S donde S es cualquier matriz de índice único. Presentamos ahora un ejemplo de esta forma general de la para declaración. Nuestro objetivo es encontrar un número de entradas en una matriz de índice único (fila) que sean positivas. Como antes, podemos escribir el bucle for basado en un índice como

    >> scalars = [1,-3,1,2,-5];
    >> num_pos = 0;
    >> for i = 1:length(scalars)
        if (scalars(i) > 0)
            num_pos = num_pos + 1;
        end
       end
    >> num_pos
    num_pos =
        3
    >>
    

    que también da el resultado correcto. En esta segunda forma, dentro del bucle for, el argumento de bucle sca se establece primero en el primer elemento de escalares, 1, sca se establece luego en el segundo elemento de escalares, -3, y así sucesivamente.

    Podemos interpretar la forma (restrictiva) de la sentencia for para VARCOUNTER = LIM_1:INC:LIM_2 dentro de esta forma general: LIM_1:INC:LIM_2 primero produce una matriz de índice único [LIM_1, LIM_1+INC,..., LIM_2] (asumiendo LIM_2 = LIM_1 + m*inc para algún entero m); luego el for asigna sucesivamente un elemento de la matriz a VARCOUNTER. (Observamos que el argumento de 82 el bucle for debe ser una matriz de índice único de fila, no una matriz de índice único de columna. Revisamos este punto en nuestra discusión de la declaración for usando matrices de doble índice.)


    This page titled 5.1: Arreglos de punto flotante de índice único is shared under a CC BY-NC-SA 4.0 license and was authored, remixed, and/or curated by Masayuki Yano, James Douglass Penn, George Konidaris, & Anthony T Patera (MIT OpenCourseWare) via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.