Creación e inicialización de matrices y vectores

Hay varias formas de declarar e inicializar matrices y vectores, que pueden dividirse en varias categorías según su finalidad.

  • Declaración sin especificar el tamaño
  • Declaración con el tamaño especificado
  • Declaración con inicialización
  • Métodos de creación estática
  • Métodos no estáticos de (re)configuración e inicialización

El método de creación más sencillo es una declaración sin especificar el tamaño, es decir, sin asignar memoria para los datos. Para ello, basta con especificar el tipo y el nombre de la variable:

matrix         matrix_a;   // matrix of type double
matrix<doublematrix_a1;  // double type matrix inside function or class templates
matrix<float>  matrix_a2;  // float matrix
vector         vector_v;   // vector of type double
vector<doublevector_v1;  // another notation of a double-type vector creation
vector<float>  vector_v2;  // vector of type float

A continuación, puede cambiar el tamaño de los objetos creados y rellenarlos con los valores deseados. También pueden utilizarse en métodos matriciales y vectoriales integrados para obtener los resultados de los cálculos. Todos estos métodos se analizarán por grupos en las secciones de este capítulo.

Puede declarar una matriz o un vector con un tamaño especificado. Esto asignará memoria pero sin ninguna inicialización. Para ello, después del nombre de la variable entre paréntesis, especifique el tamaño o tamaños (para una matriz, el primero es el número de filas y el segundo, el de columnas):

matrix         matrix_a(128128);      // you can specify as parameters
matrix<doublematrix_a1(nRowsnCols); // both constants and variables
matrix<float>  matrix_a2(nRows1);     // analog of column vector
vector         vector_v(256);
vector<doublevector_v1(nSize);
vector<float>  vector_v2(nSize +16);    // expression as a parameter

La tercera forma de crear objetos es mediante una declaración con inicialización. Los tamaños de matrices y vectores en este caso vienen determinados por la secuencia de inicialización indicada entre llaves:

matrix         matrix_a = {{0.10.20.3}, {0.40.50.6}};
matrix<doublematrix_a1 =matrix_a;     // must be matrices of the same type
matrix<float>  matrix_a2 = {{12}, {34}};
vector         vector_v = {-5, -4, -3, -2, -1012345};
vector<doublevector_v1 = {152.43.3};
vector<float>  vector_v2 =vector_v1;    // must be vectors of the same type

También existen métodos estáticos para crear matrices y vectores de un tamaño especificado con inicialización de una determinada manera (específicamente para una u otra forma canónica). Todos ellos se enumeran a continuación y tienen prototipos similares (los vectores sólo se diferencian de las matrices en la ausencia de una segunda dimensión).

static matrix<T> matrix<T>::Eye∫Tri(const ulong rows, const ulong cols, const int diagonal = 0);

static matrix<T> matrix<T>::Identity∫Ones∫Zeros(const ulong rows, const ulong cols);

static matrix<T> matrix<T>::Full(const ulong rows, const ulong cols, const double value);

  • Eye construye una matriz con unos en la diagonal especificada y ceros en el resto.
  • Tri construye una matriz con unos en y por debajo de la diagonal especificada y ceros en el resto.
  • Identity construye una matriz de identidad del tamaño especificado.
  • Ones construye una matriz (o vector) llena de unos.
  • Zeros construye una matriz (o vector) llena de ceros.
  • Full construye una matriz (o vector) con el valor dado en todos los elementos.

Si es necesario, puede convertir cualquier matriz existente en una matriz de identidad, para lo cual debe aplicar un método no estático Identity (sin parámetros).

Demostremos los métodos en acción:

matrix         matrix_a = matrix::Eye(451);
matrix<doublematrix_a1 = matrix::Full(34M_PI);
matrixf        matrix_a2 = matrixf::Identity(55);
matrixf<floatmatrix_a3 = matrixf::Ones(55);
matrix         matrix_a4 = matrix::Tri(45, -1);
vector         vector_v = vector::Ones(256);
vectorf        vector_v1 = vector<float>::Zeros(16);
vector<float>  vector_v2 = vectorf::Full(128float_value);

Además, existen métodos no estáticos para inicializar una matriz/vector con valores dados: Init y Fill.

void matrix<T>::Init(const ulong rows, const ulong cols, func_reference rule = NULL, ...)

void matrix<T>::Fill(const T value)

Una ventaja importante del método Init (que también está presente para los constructores) es la posibilidad de especificar en los parámetros una función de inicialización para rellenar los elementos de una matriz/vector según una ley determinada (véase el ejemplo siguiente).

Se puede pasar una referencia a dicha función después de los tamaños especificando su identificador sin comillas en el parámetro rules (no se trata de un puntero en el sentido de typedef (*pointer)(...) y tampoco una cadena con un nombre).

La función de inicialización debe tener una referencia al objeto que se está rellenando como primer parámetro y también puede tener parámetros adicionales: en este caso, los valores para los mismos se pasan a Init o a un constructor tras la referencia a la función. Si no se especifica el enlace rule, se creará simplemente una matriz de las dimensiones especificadas.

El método Init también permite cambiar la configuración de la matriz.

Veamos todo lo anterior con pequeños ejemplos.

matrix m(22);
m.Fill(10);
Print("matrix m \n"m);
/*
  matrix m
  [[10,10]
  [10,10]]
*/
m.Init(46);
Print("matrix m \n"m);
/*
  matrix m
  [[10,10,10,10,0.0078125,32.00000762939453]
  [0,0,0,0,0,0]
  [0,0,0,0,0,0]
  [0,0,0,0,0,0]]
*/

Aquí se utilizó el método Init para redimensionar una matriz ya inicializada, lo que ha dado lugar a que los nuevos elementos se rellenen con valores aleatorios.

La siguiente función rellena la matriz con números que aumentan exponencialmente:

template<typename T>
void MatrixSetValues(matrix<T> &mconst T initial = 1)
{
   T value = initial;
   for(ulong r = 0r < m.Rows(); r++)
   {
      for(ulong c = 0c < m.Cols(); c++)
      {
         m[r][c] = value;
         value *= 2;
      }
   }
}

A continuación, puede utilizarse para crear una matriz.

void OnStart()
{
   matrix M(36MatrixSetValues);
   Print("M = \n"M);
}

El resultado de la ejecución es:

M = 
[[1,2,4,8,16,32]
 [64,128,256,512,1024,2048]
 [4096,8192,16384,32768,65536,131072]]

En este caso, los valores para el parámetro de la función de inicialización no se especificaron a continuación de su identificador en la llamada al constructor, por lo que se utilizó el valor por defecto (1). Pero podemos, por ejemplo, pasar un valor inicial de -1 para la misma MatrixSetValues, lo que llenará la matriz con una fila negativa.

   matrix M(36MatrixSetValues, -1);