
Matrices y vectores en MQL5
Para trabajar con datos ordenados del mismo tipo, se suelen usar arrays que permiten acceder a cada elemento según su índice. Las matrices se usan ampliamente para resolver muchos problemas de álgebra lineal, modelado matemático, aprendizaje automático, etcétera. La solución a estos problemas, en términos generales, se basa en operaciones matemáticas que usan matrices y vectores, permitiendo reducir transformaciones muy complejas a una fórmula compacta y sencilla. La programación de este tipo de operaciones requiere no solo un buen nivel de conocimientos matemáticos, sino también la capacidad de escribir ciclos anidados complejos. Depurar y encontrar errores en dichos programas puede resultar agotador.
Los tipos de datos especiales matriz y vector nos permiten escribir un código próximo a la notación matemática y eliminan la necesidad de crear ciclos anidados. El programador ya no necesita recordar la indexación correcta de los arrays involucrados en el cálculo. En este artículo, mostraremos cómo crear, inicializar y aplicar los objetos matrix y vector en MQL5.
El tipo vector
vector es un array unidimensional de tipo double. Sobre los vectores se definen las operaciones de suma y multiplicación, y se introduce el concepto de "Norma" para obtener la longitud o módulo de un vector. En programación, los vectores se suelen representar usando arrays de elementos homogéneos, sobre los que no se pueden establecer las operaciones habituales con vectores, es decir, los arrays no se pueden sumar ni multiplicar entre sí, y tampoco existe el concepto de norma.
En matemáticas, los vectores se pueden representar como un vector de fila, es decir, como un array de una fila y n columnas, o como un vector de columna: como un array de una columna y n filas. En MQL5, el tipo "vector" no se divide en vectores de fila y vectores de columna, por lo que el propio programador debe comprender qué tipo de vector se usa al realizar una operación en particular.
Podemos crear e inicializar un vector usando métodos integrados.
Métodos | Análogo de NumPy | Descripción |
---|---|---|
void vector.Init( ulong size); | Crea un vector de la longitud especificada sin valores definidos | |
static vector vector::Ones(ulong size); | ones | Crea un vector de la longitud especificada y lo rellena con unidades |
static vector vector::Zeros(ulong size); | zeros | Crea un vector de la longitud especificada y lo rellena con ceros |
static vector vector::Full(ulong size,double value); | full | Crea un vector de la longitud especificada y lo rellena con el valor dado |
operador = | Retorna la copia de un vector | |
void vector.Resize(const vector v); | Cambia el tamaño de un vector añadiendo nuevos valores desde el final |
Ejemplos de creación de vectores:
void OnStart() { //--- vector initialization examples vector v; v.Init(7); Print("v = ", v); vector v1=vector::Ones(5); Print("v1 = ", v1); vector v2=vector::Zeros(3); Print("v2 = ", v2); vector v3=vector::Full(6, 2.5); Print("v3 = ", v3); vector v4{1, 2, 3}; Print("v4 = ", v4); v4.Resize(5); Print("after Resize(5) v4 = ", v4); vector v5=v4; Print("v5 = ", v5); v4.Fill(7); Print("v4 = ", v4, " v5 =",v5); } /* Execution result v = [4,5,6,8,10,12,12] v1 = [1,1,1,1,1] v2 = [0,0,0] v3 = [2.5,2.5,2.5,2.5,2.5,2.5] v4 = [1,2,3] after Resize(5) v4 = [1,2,3,7,7] v5 = [1,2,3,7,7] v4 = [7,7,7,7,7] v5 =[1,2,3,7,7] */
El método Init se puede usar no solo para asignar memoria a un vector, sino también para inicializar los elementos del vector con valores usando una función. En este caso, el tamaño del vector se transmite a Init como primer parámetro, y el nombre de la función como segundo. Si la función, a su vez, dispone de parámetros, estos parámetros se indicarán inmediatamente después del nombre de la función, separados por comas.
La función en sí debe contener como primer parámetro un enlace al vector que se le transmite, pero al llamar a Init, no será necesario transmitir el vector. Vamos a mostrar esto con el ejemplo de la función Arange, que imita a numpy.arange.
void OnStart() { //--- vector v; v.Init(7,Arange,10,0,0.5); // 3 parameters are passed with Arange call Print("v = ", v); Print("v.size = ",v.Size()); } //+------------------------------------------------------------------+ //| Values are generated within the half-open interval [start, stop)| //+------------------------------------------------------------------+ void Arange(vector& v, double stop, double start = 0, double step = 1) // the function has 4 parameters { if(start >= stop) { PrintFormat("%s wrong parameters! start=%G stop=%G", __FILE__,start, stop); return; } //--- int size = (int)((stop - start) / step); v.Resize(size); double value = start; for(ulong i = 0; i < v.Size(); i++) { v[i] = value; value += step; } } /* Execution result v = [0,0.5,1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5] v.size = 20 */
La función Arange tiene dos parámetros opcionales: "start" y "step". Por consiguiente, podemos mostrar esta forma de llamar a Init(7,Arange,10) y los resultados correspondientes de la forma que sigue:
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- vector v; v.Init(7,Arange,10); Print("v = ", v); Print("v.size = ",v.Size()); } ... /* v = [0,1,2,3,4,5,6,7,8,9] v.size = 10 */
Operaciones con vectores
Con vectores, podemos realizar las operaciones habituales de suma, resta, multiplicación y división usando un escalar.
//+------------------------------------------------------------------+ //| vector2_article.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- vector v= {1, 2, 3, 4, 5}; Print("Examples without saving vector changes"); Print("v = ", v); Print("v+5 = ", v+5); Print("v-Pi= ", v-M_PI); Print("v*2.0= ", v*2); Print("v/3.0= ", v/3.0); Print("Save all vector changes"); Print("v = ", v); Print("v+5 = ", v=v+5); Print("v-Pi= ", v=v-M_PI); Print("v*2.0= ", v= v*2); Print("v/3.0= ", v= v/3.0); } /* Execution result Examples without saving vector changes v = [1,2,3,4,5] v+5 = [6,7,8,9,10] v-Pi= [-2.141592653589793,-1.141592653589793,-0.1415926535897931,0.8584073464102069,1.858407346410207] v*2.0= [2,4,6,8,10] v/3.0= [0.3333333333333333,0.6666666666666666,1,1.333333333333333,1.666666666666667] Save all vector changes v = [1,2,3,4,5] v+5 = [6,7,8,9,10] v-Pi= [2.858407346410207,3.858407346410207,4.858407346410207,5.858407346410207,6.858407346410207] v*2.0= [5.716814692820414,7.716814692820414,9.716814692820414,11.71681469282041,13.71681469282041] v/3.0= [1.905604897606805,2.572271564273471,3.238938230940138,3.905604897606805,4.572271564273471] */ //+------------------------------------------------------------------+
Se definen las operaciones elementales de suma, resta, multiplicación y división de dos vectores del mismo tamaño.
void OnStart() { //--- vector a = {1, 2, 3}; vector b = {2, 4, 6}; Print("a + b = ", a + b); Print("a - b = ", a - b); Print("a * b = ", a * b); Print("b / a = ", b / a); } /* Execution result a + b = [3,6,9] a - b = [-1,-2,-3] a * b = [2,8,18] b / a = [2,2,2] */
Asimismo, se determinan cuatro operaciones de producto de los vectores.
void OnStart() { //--- vector a={1, 2, 3}; vector b={4, 5, 6}; Print("a = ", a); Print("b = ", b); Print("1) a.Dot(b) = ", a.Dot(b)); Print("2) a.MatMul(b) = ", a.MatMul(b)); Print("3) a.Kron(b) = ", a.Kron(b)); Print("4) a.Outer(b) = \n", a.Outer(b)); } /* Execution result a = [1,2,3] b = [4,5,6] 1) a.Dot(b) = 32.0 2) a.MatMul(b) = 32.0 3) a.Kron(b) = [[4,5,6,8,10,12,12,15,18]] 4) a.Outer(b) = [[4,5,6] [8,10,12] [12,15,18]] */
Como podemos ver en el ejemplo, el método Outer() retorna una matriz en la que el número de filas y columnas se corresponde con el tamaño de los vectores multiplicados. Los métodos Dot() y MatMul() funcionan de la misma forma. Esto supone que en la operación Outer(), el vector de la izquierda será vertical y el vector de la derecha, horizontal. Para los métodos Dot() y MatMul(), el orden de los vectores se invierte: el vector horizontal a la izquierda y el vector vertical a la derecha.
Norma vectorial
Sobre los vectores y matrices se define la noción de norma, que representa el concepto de longitud o valor absoluto de un vector. Hay tres opciones para calcular la norma vectorial, enumeradas en ENUM_VECTOR_NORM.void OnStart() { //--- struct str_vector_norm { ENUM_VECTOR_NORM norm; int value; }; str_vector_norm vector_norm[]= { {VECTOR_NORM_INF, 0}, {VECTOR_NORM_MINUS_INF, 0}, {VECTOR_NORM_P, 0}, {VECTOR_NORM_P, 1}, {VECTOR_NORM_P, 2}, {VECTOR_NORM_P, 3}, {VECTOR_NORM_P, 4}, {VECTOR_NORM_P, 5}, {VECTOR_NORM_P, 6}, {VECTOR_NORM_P, 7}, {VECTOR_NORM_P, -1}, {VECTOR_NORM_P, -2}, {VECTOR_NORM_P, -3}, {VECTOR_NORM_P, -4}, {VECTOR_NORM_P, -5}, {VECTOR_NORM_P, -6}, {VECTOR_NORM_P, -7} }; vector v{1, 2, 3, 4, 5, 6, 7}; double norm; Print("v = ", v); //--- for(int i=0; i<ArraySize(vector_norm); i++) { switch(vector_norm[i].norm) { case VECTOR_NORM_INF : norm=v.Norm(VECTOR_NORM_INF); Print("v.Norm(VECTOR_NORM_INF) = ", norm); break; case VECTOR_NORM_MINUS_INF : norm=v.Norm(VECTOR_NORM_MINUS_INF); Print("v.Norm(VECTOR_NORM_MINUS_INF) = ", norm); break; case VECTOR_NORM_P : norm=v.Norm(VECTOR_NORM_P, vector_norm[i].value); PrintFormat("v.Norm(VECTOR_NORM_P,%d) = %G", vector_norm[i].value, norm); } } } /* v = [1,2,3,4,5,6,7] v.Norm(VECTOR_NORM_INF) = 7.0 v.Norm(VECTOR_NORM_MINUS_INF) = 1.0 v.Norm(VECTOR_NORM_P,0) = 7 v.Norm(VECTOR_NORM_P,1) = 28 v.Norm(VECTOR_NORM_P,2) = 11.8322 v.Norm(VECTOR_NORM_P,3) = 9.22087 v.Norm(VECTOR_NORM_P,4) = 8.2693 v.Norm(VECTOR_NORM_P,5) = 7.80735 v.Norm(VECTOR_NORM_P,6) = 7.5473 v.Norm(VECTOR_NORM_P,7) = 7.38704 v.Norm(VECTOR_NORM_P,-1) = 0.385675 v.Norm(VECTOR_NORM_P,-2) = 0.813305 v.Norm(VECTOR_NORM_P,-3) = 0.942818 v.Norm(VECTOR_NORM_P,-4) = 0.980594 v.Norm(VECTOR_NORM_P,-5) = 0.992789 v.Norm(VECTOR_NORM_P,-6) = 0.99714 v.Norm(VECTOR_NORM_P,-7) = 0.998813 */
Usando la norma, podemos medir la distancia entre dos vectores:
void OnStart() { //--- vector a{1,2,3}; vector b{2,3,4}; double distance=(b-a).Norm(VECTOR_NORM_P,2); Print("a = ",a); Print("b = ",b); Print("|a-b| = ",distance); } /* Execution result a = [1,2,3] b = [2,3,4] |a-b| = 1.7320508075688772 */
El tipo matrix
El vector es un caso especial de matriz, que en realidad es un array bidimensional de tipo doble. Así, podemos decir que una matriz es un array de vectores del mismo tamaño. El número de filas en la matriz es igual al número de vectores, y el número de columnas es igual a la longitud de los vectores.
Las operaciones de suma y multiplicación también se definen sobre matrices. Los lenguajes de programación convencionales usan arrays para representar matrices. Pero los arrays ordinarios no se pueden sumar ni multiplicar entre sí, y el concepto de norma no está definido para ellos. En matemáticas, se consideran muchos tipos y tipos diferentes de matrices. Por ejemplo, matrices de identidad, simétricas, asimétricas, triangulares superiores (triangulares inferiores) y otras.
Podemos crear e inicializar una matriz usando métodos integrados similares a los métodos vectoriales.
Método | Análogo en NumPy | Descripción |
---|---|---|
void static matrix.Eye(const int rows, const int cols, const int ndiag=0) | Crea una matriz con unidades a lo largo de la diagonal indicada y ceros en los demás lugares | |
void matrix.Identity() | Rellena una matriz con unidades en la diagonal principal y ceros en los demás lugares | |
void static matrix.Ones(const int rows, const int cols) | Crea una nueva matriz según el número de filas y columnas, rellena de unidades | |
void static matrix.Zeros(const int rows, const int cols) | Crea una nueva matriz según el número de filas y columnas, rellena de ceros | |
void static matrix.Tri(const int rows, const int cols, const int ndiag=0) | tri | Crea una matriz con unidades en la diagonal indicada y por debajo, y ceros en los demás lugares |
void matrix.Diag(const vector v, const int ndiag=0) | diag | Extrae la diagonal o crea una matriz diagonal |
void matrix.Full(const int rows, const int cols, const scalar value) | Crea una nueva matriz según el número de filas y columnas, rellena de valores escalares | |
void matrix.Fill(const scalar value) | Rellena una matriz con el valor indicado |
Ejemplos de creación y rellenado de matrices:
void OnStart() { //--- matrix m{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; Print("m = \n", m); matrix ones=matrix::Ones(4, 4); Print("ones = \n", ones); matrix zeros=matrix::Zeros(4, 4); Print("zeros = \n", zeros); matrix eye=matrix::Eye(4, 4); Print("eye = \n", eye); matrix identity(4, 5); Print("matrix_identity\n", identity); identity.Identity(); Print("matrix_identity\n", identity); matrix tri=matrix::Tri(3, 4); Print("tri = \n", tri); Print("tri.Transpose() = \n", tri.Transpose()); // transpose the matrix matrix diag(5, 5); Print("diag = \n", diag); vector d{1, 2, 3, 4, 5}; diag.Diag(d); Print("diag = \n", diag); // insert values from the vector into the matrix diagonal matrix fill(5, 5); fill.Fill(10); Print("fill = \n", fill); matrix full =matrix::Full(5, 5, 100); Print("full = \n", full); matrix init(5, 7); Print("init = \n", init); m.Init(4, 6); Print("init = \n", init); matrix resize=matrix::Full(2, 2, 5); resize.Resize(5,5); Print("resize = \n", resize); } /* Execution result m = [[1,2,3] [4,5,6] [7,8,9]] ones = [[1,1,1,1] [1,1,1,1] [1,1,1,1] [1,1,1,1]] zeros = [[0,0,0,0] [0,0,0,0] [0,0,0,0] [0,0,0,0]] eye = [[1,0,0,0] [0,1,0,0] [0,0,1,0] [0,0,0,1]] matrix_identity [[1,0,0,0,0] [0,1,0,0,0] [0,0,1,0,0] [0,0,0,1,0]] matrix_identity [[1,0,0,0,0] [0,1,0,0,0] [0,0,1,0,0] [0,0,0,1,0]] tri = [[1,0,0,0] [1,1,0,0] [1,1,1,0]] tri.Transpose() = [[1,1,1] [0,1,1] [0,0,1] [0,0,0]] diag = [[0,0,0,0,0] [0,0,0,0,0] [0,0,0,0,0] [0,0,0,0,0] [0,0,0,0,0]] diag = [[1,0,0,0,0] [0,2,0,0,0] [0,0,3,0,0] [0,0,0,4,0] [0,0,0,0,5]] fill = [[10,10,10,10,10] [10,10,10,10,10] [10,10,10,10,10] [10,10,10,10,10] [10,10,10,10,10]] full = [[100,100,100,100,100] [100,100,100,100,100] [100,100,100,100,100] [100,100,100,100,100] [100,100,100,100,100]] resize = [[5,5,0,0,0] [5,5,0,0,0] [0,0,0,0,0] [0,0,0,0,0] [0,0,0,0,0]] */
El siguiente ejemplo muestra cómo podemos usar funciones personalizadas al rellenar matrices:
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- matrix random(4, 5, MatrixRandom); Print("random = \n",random); matrix init(3, 6, MatrixSetValues); Print("init = \n", init); } //+------------------------------------------------------------------+ //| Fills the matrix with random values | //+------------------------------------------------------------------+ void MatrixRandom(matrix& m) { for(ulong r=0; r<m.Rows(); r++) { for(ulong c=0; c<m.Cols(); c++) { m[r][c]=double(MathRand())/32767.; } } } //+------------------------------------------------------------------+ //| Fills the matrix with powers of a number | //+------------------------------------------------------------------+ void MatrixSetValues(matrix& m, double initial=1) { double value=initial; for(ulong r=0; r<m.Rows(); r++) { for(ulong c=0; c<m.Cols(); c++) { m[r][c]=value; value*=2; } } } /* Execution result random = [[0.4200262459181494,0.5014496292001098,0.7520371105075229,0.652058473464156,0.08783227027191992] [0.5991088595233008,0.4311960203863643,0.8718832972197638,0.1350138859218116,0.901882992034669] [0.4964445936460463,0.8354747154148991,0.5258339182714317,0.6055482650227363,0.5952940458388012] [0.3959166234321116,0.8146916104617451,0.2053590502639851,0.2657551805169835,0.3672292245246742]] init = [[1,2,4,8,16,32] [64,128,256,512,1024,2048] [4096,8192,16384,32768,65536,131072]] */
Además, debemos tener en cuenta que hay dos formas de crear una matriz sin inicializar los valores.
//--- create a matrix of a given 'rows x cols' size matrix m(3, 3); // ------ equivalent matrix m; m.Resize(3, 3);
Norma de la matriz
En total, hay nueve opciones para calcular la norma de la matriz. Se enumeran en ENUM_MATRIX_NORM.
void OnStart() { //--- ENUM_MATRIX_NORM matrix_norm[]= {MATRIX_NORM_FROBENIUS, MATRIX_NORM_SPECTRAL, MATRIX_NORM_NUCLEAR, MATRIX_NORM_INF, MATRIX_NORM_MINUS_INF, MATRIX_NORM_P1, MATRIX_NORM_MINUS_P1, MATRIX_NORM_P2, MATRIX_NORM_MINUS_P2 }; matrix m{{1,2,3},{4,5,6},{7,8,9}}; Print("matrix m:\n",m); //--- compute the norm using all ways double norm; for(int i=0; i<ArraySize(matrix_norm); i++) { norm=m.Norm(matrix_norm[i]); PrintFormat("%d. Norm(%s) = %.6f",i+1, EnumToString(matrix_norm[i]),norm); } //--- return; } /* Execution result matrix m: [[1,2,3] [4,5,6] [7,8,9]] 1. Norm(MATRIX_NORM_FROBENIUS) = 16.881943 2. Norm(MATRIX_NORM_SPECTRAL) = 14.790157 3. Norm(MATRIX_NORM_NUCLEAR) = 17.916473 4. Norm(MATRIX_NORM_INF) = 24.000000 5. Norm(MATRIX_NORM_MINUS_INF) = 6.000000 6. Norm(MATRIX_NORM_P1) = 18.000000 7. Norm(MATRIX_NORM_MINUS_P1) = 12.000000 8. Norm(MATRIX_NORM_P2) = 16.848103 9. Norm(MATRIX_NORM_MINUS_P2) = 0.000000 */
Operaciones con matrices y vectores
Para resolver problemas matemáticos, las matrices contienen métodos especiales:
- Transposición
- Suma, resta, multiplicación y división de matrices elemento a elemento
- Suma, resta, multiplicación y división de elementos de matriz por un escalar
- Producto de matrices y vectores por el método (matrix product)
- Inner()
- Outer()
- Kron()
- Inv() — matriz inversa
- Solve() — resuelve un sistema de ecuaciones lineales
- LstSq() — resuelve un sistema de ecuaciones lineales usando el método de mínimos cuadrados (para matrices no cuadradas o degeneradas)
- PInv() — matriz pseudo-inversa de mínimos cuadrados
- Trabajo con columnas, filas y diagonales
Descomposición de matrices:
Método | Análogo en NumPy | Descripción |
---|---|---|
bool matrix.Cholesky(matrix& L) | cholesky | Calcula la descomposición de Cholesky. |
bool matrix.QR(matrix& Q, matrix& R) | qr | Calcula la descomposición QR |
bool matrix.SVD(matrix& U, matrix& V, vector& singular_values) | Calcula la descomposición SVD | |
bool matrix.Eig(matrix& eigen_vectors, vector& eigen_values) | Calcula los valores propios y los vectores propios derechos de una matriz cuadrada | |
bool matrix.EigVals(vector& eigen_values) | Calcula los valores propios de una matriz genérica | |
bool matrix.LU(matrix& L, matrix& U) |
| Realiza una descomposición LU de una matriz como el producto de una matriz triangular inferior y una matriz triangular superior |
bool matrix.LUP(matrix& L, matrix& U, matrix& P) |
| Realiza una descomposición LUP con rotación parcial, que supone una factorización LU con permutaciones de fila: PA = LU |
Producto de matrices y vectores
Para realizar el producto matricial de matrices y vectores, se define el método MatMul(). A menudo se usa para resolver muchos problemas matemáticos. Al multiplicar una matriz y un vector, solo se permiten dos opciones:
- El vector horizontal de la izquierda se multiplica por la matriz de la derecha, la longitud del vector es igual al número de columnas de la matriz;
- La matriz de la izquierda se multiplica por el vector vertical de la derecha, el número de columnas de la matriz es igual a la longitud del vector.
Si la longitud del vector no es igual al número de columnas de la matriz, se generará un error crítico de tiempo de ejecución.
Las matrices del tipo A[M,N] * B[N,K] = C[M,K] se pueden multiplicar entre sí, es decir, el número de columnas en la matriz de la izquierda debe ser igual al número de filas en la matriz de la derecha. Si las dimensiones no son consistentes, el resultado será una matriz vacía. Vamos a mostrar con ejemplos todas las variantes del producto de matriz.
void OnStart() { //--- initialize matrices matrix m35, m52; m35.Init(3,5,Arange); m52.Init(5,2,Arange); //--- Print("1. Product of horizontal vector v[3] and matrix m[3,5]"); vector v3 = {1,2,3}; Print("On the left v3 = ",v3); Print("On the right m35 = \n",m35); Print("v3.MatMul(m35) = horizontal vector v[5] \n",v3.MatMul(m35)); //--- show that this is really a horizontal vector Print("\n2. Product of matrix m[1,3] and matrix m[3,5]"); matrix m13; m13.Init(1,3,Arange,1); Print("On the left m13 = \n",m13); Print("On the right m35 = \n",m35); Print("m13.MatMul(m35) = matrix m[1,5] \n",m13.MatMul(m35)); Print("\n3. Product of matrix m[3,5] and vertical vector v[5]"); vector v5 = {1,2,3,4,5}; Print("On the left m35 = \n",m35); Print("On the right v5 = ",v5); Print("m35.MatMul(v5) = vertical vector v[3] \n",m35.MatMul(v5)); //--- show that this is really a vertical vector Print("\n4. Product of matrix m[3,5] and matrix m[5,1]"); matrix m51; m51.Init(5,1,Arange,1); Print("On the left m35 = \n",m35); Print("On the right m51 = \n",m51); Print("m35.MatMul(m51) = matrix v[3] \n",m35.MatMul(m51)); Print("\n5. Product of matrix m[3,5] and matrix m[5,2]"); Print("On the left m35 = \n",m35); Print("On the right m52 = \n",m52); Print("m35.MatMul(m52) = matrix m[3,2] \n",m35.MatMul(m52)); Print("\n6. Product of horizontal vector v[5] and matrix m[5,2]"); Print("On the left v5 = \n",v5); Print("On the right m52 = \n",m52); Print("v5.MatMul(m52) = horizontal vector v[2] \n",v5.MatMul(m52)); Print("\n7. Outer() product of horizontal vector v[5] and vertical vector v[3]"); Print("On the left v5 = \n",v5); Print("On the right v3 = \n",v3); Print("v5.Outer(v3) = matrix m[5,3] \n",v5.Outer(v3)); //--- show that the product of matrices generates the same result Print("\n8. Outer() product of the matrix m[1,5] and matrix m[3,1]"); matrix m15,m31; m15.Init(1,5,Arange,1); m31.Init(3,1,Arange,1); Print("On the left m[1,5] = \n",m15); Print("On the right m31 = \n",m31); Print("m15.Outer(m31) = matrix m[5,3] \n",m15.Outer(m31)); } //+------------------------------------------------------------------+ //| Fill the matrix with increasing values | //+------------------------------------------------------------------+ void Arange(matrix & m, double start = 0, double step = 1) // the function has three parameters { //--- ulong cols = m.Cols(); ulong rows = m.Rows(); double value = start; for(ulong r = 0; r < rows; r++) { for(ulong c = 0; c < cols; c++) { m[r][c] = value; value += step; } } //--- } /* Execution result 1. Product of horizontal vector v[3] and matrix m[3,5] On the left v3 = [1,2,3] On the right m35 = [[0,1,2,3,4] [5,6,7,8,9] [10,11,12,13,14]] v3.MatMul(m35) = horizontal vector v[5] [40,46,52,58,64] 2. Product of matrix m[1,3] and matrix m[3,5] On the left m13 = [[1,2,3]] On the right m35 = [[0,1,2,3,4] [5,6,7,8,9] [10,11,12,13,14]] m13.MatMul(m35) = matrix m[1,5] [[40,46,52,58,64]] 3. Product of matrix m[3,5] and vertical vector v[5] On the left m35 = [[0,1,2,3,4] [5,6,7,8,9] [10,11,12,13,14]] On the right v5 = [1,2,3,4,5] m35.MatMul(v5) = vertical vector v[3] [40,115,190] 4. Product of matrix m[3,5] and matrix m[5,1] On the left m35 = [[0,1,2,3,4] [5,6,7,8,9] [10,11,12,13,14]] On the right m51 = [[1] [2] [3] [4] [5]] m35.MatMul(m51) = matrix v[3] [[40] [115] [190]] 5. Product of matrix m[3,5] and matrix m[5,2] On the left m35 = [[0,1,2,3,4] [5,6,7,8,9] [10,11,12,13,14]] On the right m52 = [[0,1] [2,3] [4,5] [6,7] [8,9]] m35.MatMul(m52) = matrix m[3,2] [[60,70] [160,195] [260,320]] 6. The product of horizontal vector v[5] and matrix m[5,2] On the left v5 = [1,2,3,4,5] On the right m52 = [[0,1] [2,3] [4,5] [6,7] [8,9]] v5.MatMul(m52) = horizontal vector v[2] [80,95] 7. Outer() product of horizontal vector v[5] and vertical vector v[3] On the left v5 = [1,2,3,4,5] On the right v3 = [1,2,3] v5.Outer(v3) = matrix m[5,3] [[1,2,3] [2,4,6] [3,6,9] [4,8,12] [5,10,15]] 8. Outer() product of the matrix m[1,5] and matrix m[3,1] On the left m[1,5] = [[1,2,3,4,5]] On the right m31 = [[1] [2] [3]] m15.Outer(m31) = matrix m[5,3] [[1,2,3] [2,4,6] [3,6,9] [4,8,12] [5,10,15]] */
Para comprender mejor el funcionamiento interno de los tipos matrix y vector, estos ejemplos muestran cómo podemos usar matrices en lugar de vectores o, en otras palabras, que los vectores se pueden representar como matrices.
Números complejos — el tipo complex
struct complex { double real; // real part double imag; // imaginary part };El tipo "complex" puede transmitirse usando un valor como parámetro para las funciones MQL5 (a diferencia de las estructuras normales, que se transmiten mediante un enlace). Para las funciones importadas desde una DLL, el tipo "complex" debe transmitirse solo mediante un enlace.
Para describir las constantes complejas, se usa el sufijo 'i':
complex square(complex c) { return(c*c); } void OnStart() { Print(square(1+2i)); // a constant is passed as a parameter } // will print "(-3,4)" - a string representation of a complex numberPara los números complejos, solo están disponibles operaciones sencillas: =, +, -, *, /, +=, -=, *=, /=, ==, !=.
Próximamente se añadirá el soporte de números complejos en las matrices y vectores, así como funciones matemáticas adicionales: obtención del valor absoluto, seno, coseno y muchas otras.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/9805





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso