Матрицы и векторы в MQL5

11 февраля 2022, 15:28
MetaQuotes
8
1 693

Для работы с упорядоченными однотипными данными обычно используются массивы, которые позволяет обращаться к каждому элементу по индексу. Массивы широко используются при решении множества задач из линейной алгебры, математического моделирования, в машинном обучении и т.д. Решение этих задач, в общем виде, основывается на математических операциях с использованием матриц и векторов, которые позволяют компактно записывать в виде простой формулы очень сложные преобразования. Для программирования таких операций нужен не только хороший уровень знания математики, но и умение писать сложные вложенные циклы. Отладка и поиск ошибок в таких программах могут быть очень изнуряющими. 

Специальные типы данных matrix и vector позволяют писать код, приближенный к математической записи, и избавляют от необходимости создавать вложенные циклы. Программисту уже не нужно помнить о правильной индексации массивов, которые участвуют в вычислении. В этой статье мы покажем, как создавать, инициализировать и применять объекты matrix и vector в MQL5.


Тип vector

vector представляет из себя одномерный массив типа double. Над векторами определены операции сложения и умножения, а также введено понятие "Норма" для получения длины или модуля вектора. В программировании векторы обычно представлены массивами однородных элементов, над которыми может быть не задано обычных векторных операций, то есть массивы нельзя складывать друг с другом, умножать друг на друга, а также нет понятия нормы.

В математике векторы могут быть представлены как вектор-строка, то есть как массив из одной строки и n столбцов, или как вектор-столбец как матрица из одного столбца и n строк. В MQL5 тип "vector" не разделяется на векторы-строки и векторы-столбцы, поэтому программист сам должен понимать, какой тип вектора используется при выполнении той или иной операции.

Создать и инициализировать вектор можно с помощью встроенных методов.

Методы
Аналог NumPy
Описание
void vector.Init( ulong size);   Создает вектор указанной длины, значения в котором не определены
static vector vector::Ones(ulong size);
 ones
Создает вектор указанной длины и заполняет его единицами
static vector vector::Zeros(ulong size);
 zeros
Создает вектор указанной длины и заполняет его нулями
static vector vector::Full(ulong size,double value);
 full
Создает вектор указанной длины и заполняет его заданным значением
оператор =

Возвращает копию вектора
void vector.Resize(const vector v);
  Изменяет размер вектора путем добавления новых значений с конца 


Примеры создания вектора:

void OnStart()
 {
//--- примеры инициализации векторов
  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);  
   
 }
 
 
/*
Результат выполнения

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]

*/ 

Метод Init может использоваться не только для распределения памяти под вектор, но также и для инициализации элементов вектора значениями с помощью функции. В этом случае в Init первым параметром передается размер вектора, а вторым — имя функции. Если функция в свою очередь имеет параметры, то эти параметры указываются сразу за именем функции через запятую.

Сама функция должна содержать первым параметром ссылку на вектор, который передается в неё, но при вызове Init вектор передавать не нужно. Покажем это на примере функции Arange, имитирующей numpy.arange.

void OnStart()
  {
//---
   vector v;
   v.Init(7,Arange,10,0,0.5); // при вызове Arange передаются 3 параметра
   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) // функция имеет 4 параметра
  {
   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;
     }
  }
  
/*
Результат выполнения

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

*/

При этом функция Arange имеет два необязательных параметра — "start" и "step". Поэтому допустим такой способ вызова Init(7,Arange,10) с соответствующим результатом:

//+------------------------------------------------------------------+
//| 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

*/


Операции с векторами

Над векторами можно производить обычные операции сложения, вычитания, умножения и деления с использованием скаляра.

//+------------------------------------------------------------------+
//|                                              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("Примеры без сохранения изменений вектора");
  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("Cохраняем все изменения вектора");
  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);
 }
/*
Результат выполнения
   
Примеры без сохранения изменений вектора
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]
Cохраняем все изменения вектора
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]

*/
//+------------------------------------------------------------------+

Определены поэлементные операции сложения, вычитания, умножения и деления двух векторов одинакового размера.

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);
  }

/*
Результат выполнения

a + b = [3,6,9]
a - b = [-1,-2,-3]
a * b = [2,8,18]
b / a = [2,2,2]

*/

Также определены четыре операции произведения векторов.

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));
 }
/*
Результат выполнения

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]]

*/

Как видно из примера, метод Outer() возвращает матрицу, в которой количество строк и столбцов соответствуют размерам умножаемых векторов. Методы Dot() и MatMul() работают одинаково. При этом предполагается, что в операции Outer() вектор слева является вертикальным, а правый вектор — горизонтальный. Для методов Dot() и MatMul() порядок векторов обратный — слева горизонтальный вектор,  а справа — вертикальный.


Норма вектора

Над векторами и матрицами определено понятие нормы, которое представляет понятие длины или абсолютного значения вектора. Всего существует три варианта вычисления нормы вектора, которые перечислены в 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

*/

С помощью нормы можно измерять расстояние между двумя векторами:

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);   
 }
/*
Результат выполнения

a = [1,2,3]
b = [2,3,4]
|a-b| = 1.7320508075688772

*/


Тип matrix

Вектор является частным случаем матрицы, которая фактически представляет из себя двумерный массив типа double. Таким образом, можно сказать, что матрица — это массив векторов одинакового размера. Количество строк в матрице равно количеству векторов, а количество столбцов — длине векторов. 

Над матрицами также определены операции сложения и умножения. В обычных языках программирования для представления матриц используются массивы. Но обычные массивы нельзя складывать друг с другом, умножать друг на друга, а также для них не определено понятие нормы. В математике рассматривается множество различных типов и видов матриц. Например, единичная, симметричная, кососимметричная, верхнетреугольная (нижнетреугольная) и другие матрицы.

Создать и инициализировать матрицу можно с помощью встроенных методов, аналогичных методам вектора.

Метод

Аналог в NumPy

Описание

void static matrix.Eye(const int rows, const int cols, const int ndiag=0)

eye

Создает матрицу с единицами по указанной диагонали и нулями в остальных местах

void matrix.Identity()

identity

Заполняет матрицу единицами на главной диагонали и нулями в остальных местах

void static matrix.Ones(const int rows, const int cols)

ones

Создает новую матрицу по числу строк и столбцов, заполненную единицами

void static matrix.Zeros(const int rows, const int cols)

zeros

Создает новую матрицу по числу строк и столбцов, заполненную нулями

void static matrix.Tri(const int rows, const int cols, const int ndiag=0)
tri  Создает матрицу с единицами на указанной диагонали и ниже и с нулями в остальных местах 
void matrix.Diag(const vector v, const int ndiag=0)  diag  Извлекает диагональ или создает диагональную матрицу 

void matrix.Full(const int rows, const int cols, const scalar value)

full

Создает новую матрицу по числу строк и столбцов, заполненную скалярным значением

void matrix.Fill(const scalar value)     Заполняет матрицу указанным значением


Примеры создания и заполнения матриц:

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()); // транспонируем матрицу

  matrix diag(5, 5);
  Print("diag = \n", diag);
  vector d{1, 2, 3, 4, 5};
  diag.Diag(d);
  Print("diag = \n", diag); // вставим по диагонали матрицы значения из вектора

  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);  
 }
/*
Результат выполнения

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]]

*/

Следующий пример показывает, как можно использовать собственные функции при заполнении матриц:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
 {
//---
  matrix random(4, 5, MatrixRandom);
  Print("random = \n",random);
  
  matrix init(3, 6, MatrixSetValues);  
  Print("init = \n", init);
  
 }
//+------------------------------------------------------------------+
//| Заполняет матрицу случайными значениями                          |
//+------------------------------------------------------------------+
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.;
     }
   }
 }
//+------------------------------------------------------------------+
//| Заполняет матрицу степенями числа                                |
//+------------------------------------------------------------------+
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;
     }
   }
 } 
 
/*
Результат выполнения

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]]

*/ 

Кроме того, обратите, внимание, что создать матрицу без инициализации значениями можно двумя способами. 

//--- создание матрицы заданного размера rows x cols
  matrix m(3, 3);

// ------ эквивалентно
  matrix m;
  m.Resize(3, 3);


Норма матрицы

Всего предусмотрено девять вариантов вычисления нормы матрицы. Они перечислены в 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);
//--- вычислим норму всеми способами
   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;
  }

/*
Результат выполнения

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

*/


Операции с матрицами и векторами

Для решения математических задач матрицы содержат специальные методы:

  • Транспонирование
  • Сложение, вычитание, умножение и деление матриц поэлементно
  • Сложение, вычитание, умножение и деление элементов матрицы на скаляр
  • Произведение матриц и векторов методом MatMul  (matrix product)
  • Inner()
  • Outer()
  • Kron()
  • Inv() — обратная матрица
  • Solve() — решение системы линейных уравнений
  • LstSq() — решение системы линейных уравнений методом наименьших квадратов (для неквадратных или вырожденных матриц)
  • PInv() — псевдообратная матрица методом наименьших квадратов
  • Работа со столбцами, строками и диагоналями

Разложение матрицы:

Метод

Аналог в NumPy

Описание
bool matrix.Cholesky(matrix& L) cholesky Вычисляет декомпозицию Холецкого
bool matrix.QR(matrix& Q, matrix& R) qr

Вычисляет QR-декомпозиция

bool matrix.SVD(matrix& U, matrix& V, vector& singular_values)

svd

Вычисляет SVD-декомпозицию

bool matrix.Eig(matrix& eigen_vectors, vector& eigen_values)

eig

Вычисляет собственные значения и правые собственные векторы квадратной матрицы

bool matrix.EigVals(vector& eigen_values)

eigvals

Вычисляет собственные значения общей матрицы

bool matrix.LU(matrix& L, matrix& U)

 

Выполняет LU-декомпозицию матрицы как произведение нижней треугольной матрицы и верхней треугольной матрицы

bool matrix.LUP(matrix& L, matrix& U, matrix& P)

 

Выполняет LUP-декомпозицию с частичным поворотом, которая является LU-факторизацией с перестановками строк: PA = LU


Произведение матриц и векторов

Для выполнения матричного произведения матриц и векторов определен метод MatMul(). Он часто используется в решении многих математических задач. При умножении матрицы и вектора допустимы только два варианта:

  • Горизонтальный вектор слева умножается на матрицу справа, длина вектора равна количеству столбцов матрицы;
  • Матрица слева умножается на вертикальный вектор справа, количество столбцов матрицы равно длине вектора.

Если длина вектора не равна количеству столбцов матрицы, то будет вызвана критическая ошибка исполнения.

Умножать между собой можно матрицы вида A[M,N] * B[N,K] = С[M,K], то есть количество столбцов в матрице слева должно быть равно количеству строк в матрице справа. Если размеры не согласованы, результатом будет пустая матрица. Покажем все варианты матричного произведения на примерах.

void OnStart()
  {
//--- инициализируем матрицы
   matrix m35, m52;
   m35.Init(3,5,Arange);
   m52.Init(5,2,Arange);
//---
   Print("1. Произведение горизонтального вектора v[3] на матрицу m[3,5]");
   vector v3 = {1,2,3};
   Print("Слева v3 = ",v3);
   Print("Справа m35 = \n",m35);
   Print("v3.MatMul(m35) = горизонтальный вектор v[5] \n",v3.MatMul(m35));
//--- покажем, что это действительно горизонтальный вектор
   Print("\n2. Произведение матрицы m[1,3] на матрицу m[3,5]");
   matrix m13;
   m13.Init(1,3,Arange,1);
   Print("Слева m13 = \n",m13);
   Print("Справа m35 = \n",m35);
   Print("m13.MatMul(m35) = матрица m[1,5] \n",m13.MatMul(m35));

   Print("\n3. Произведение матрицы m[3,5] на вертикальный вектор v[5]");
   vector v5 = {1,2,3,4,5};
   Print("Слева m35 = \n",m35);
   Print("Справа v5 = ",v5);
   Print("m35.MatMul(v5) = вертикальный вектор v[3] \n",m35.MatMul(v5));
//--- покажем, что это действительно вертикальный вектор
   Print("\n4. Произведение матрицы m[3,5] на матрицу m[5,1]");
   matrix m51;
   m51.Init(5,1,Arange,1);
   Print("Слева m35 = \n",m35);
   Print("Справа m51 = \n",m51);
   Print("m35.MatMul(m51) = матрица v[3] \n",m35.MatMul(m51));

   Print("\n5. Произведение матрицы m[3,5] на матрицу m[5,2]");
   Print("Слева m35 = \n",m35);
   Print("Справа m52 = \n",m52);
   Print("m35.MatMul(m52) = матрица m[3,2] \n",m35.MatMul(m52));

   Print("\n6. Произведение горизонтального вектора v[5] на матрицу m[5,2]");
   Print("Слева v5 = \n",v5);
   Print("Справа m52 = \n",m52);
   Print("v5.MatMul(m52) = горизонтальный вектор v[2] \n",v5.MatMul(m52));

   Print("\n7. Произведение Outer() горизонтального вектора v[5] на вертикальный вектор v[3]");
   Print("Слева v5 = \n",v5);
   Print("Справа v3 = \n",v3);
   Print("v5.Outer(v3) = матрица m[5,3] \n",v5.Outer(v3));
//--- покажем, что произведение матриц дает такой же результат
   Print("\n8. Произведение Outer() матрицы m[1,5] на матрицу m[3,1]");
   matrix m15,m31;
   m15.Init(1,5,Arange,1);
   m31.Init(3,1,Arange,1);
   Print("Слева m[1,5] = \n",m15);
   Print("Справа m31 = \n",m31);
   Print("m15.Outer(m31) = матрица m[5,3] \n",m15.Outer(m31));
  }
//+------------------------------------------------------------------+
//|  Заполнение матрицы нарастающими значениями                      |
//+------------------------------------------------------------------+
void Arange(matrix & m, double start = 0, double step = 1) // функция имеет 3 параметра
  {
//---
   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;
        }
     }
//---     
  }
/*
Результат выполнения

1. Произведение горизонтального вектора v[3] на матрицу m[3,5]
Слева v3 = [1,2,3]
Справа m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
v3.MatMul(m35) = горизонтальный вектор v[5]
[40,46,52,58,64]

2. Произведение матрицы m[1,3] на матрицу m[3,5]
Слева m13 =
[[1,2,3]]
Справа m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
m13.MatMul(m35) = матрица m[1,5]
[[40,46,52,58,64]]

3. Произведение матрицы m[3,5] на вертикальный вектор v[5]
Слева m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
Справа v5 = [1,2,3,4,5]
m35.MatMul(v5) = вертикальный вектор v[3]
[40,115,190]

4. Произведение матрицы m[3,5] на матрицу m[5,1]
Слева m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
Справа m51 =
[[1]
 [2]
 [3]
 [4]
 [5]]
m35.MatMul(m51) = матрица v[3]
[[40]
 [115]
 [190]]

5. Произведение матрицы m[3,5] на матрицу m[5,2]
Слева m35 =
[[0,1,2,3,4]
 [5,6,7,8,9]
 [10,11,12,13,14]]
Справа m52 =
[[0,1]
 [2,3]
 [4,5]
 [6,7]
 [8,9]]
m35.MatMul(m52) = матрица m[3,2]
[[60,70]
 [160,195]
 [260,320]]

6. Произведение горизонтального вектора v[5] на матрицу m[5,2]
Слева v5 =
[1,2,3,4,5]
Справа m52 =
[[0,1]
 [2,3]
 [4,5]
 [6,7]
 [8,9]]
v5.MatMul(m52) = горизонтальный вектор v[2]
[80,95]

7. Произведение Outer() горизонтального вектора v[5] на вертикальный вектор v[3]
Слева v5 =
[1,2,3,4,5]
Справа v3 =
[1,2,3]
v5.Outer(v3) = матрица m[5,3]
[[1,2,3]
 [2,4,6]
 [3,6,9]
 [4,8,12]
 [5,10,15]]

8. Произведение Outer() матрицы m[1,5] на матрицу m[3,1]
Слева m[1,5] =
[[1,2,3,4,5]]
Справа m31 =
[[1]
 [2]
 [3]]
m15.Outer(m31) = матрица m[5,3]
[[1,2,3]
 [2,4,6]
 [3,6,9]
 [4,8,12]
 [5,10,15]]

*/

Для лучшего понимания внутреннего устройства типов matrix и vector в этих примерах показано, как можно использовать матрицы вместо векторов или, другими словами, что векторы можно представлять в виде матриц.


Комплексные числа — тип complex

Для решения некоторых математических задач требуется новый тип данных — комплексные числа. Тип complex представляет из себя структуру:
struct complex
  {
   double             real;   // вещественная часть
   double             imag;   // мнимая часть
  };
Тип "complex" может передаваться по значению в качестве параметра для MQL5-функций (в отличие от обычных структур, которые передаются только по ссылке). Для функций, импортируемых из DLL, тип "complex" должен передаваться только по ссылке.

Для описания комплексных констант используется суффикс 'i':
complex square(complex c)
  {
   return(c*c);
  }
  
void OnStart()
  {
   Print(square(1+2i));  // в качестве параметра передается константа
  }

// будет выведено "(-3,4)" - это строковое представление комплексного числа
Для комплексных чисел доступны только простые операции: =, +, -, *, /, +=, -=, *=, /=, ==, !=.

Скоро будут добавлена поддержка комплексных чисел в матрицах и векторах, а также дополнительные математические функции: получение абсолютного значения, синуса, косинуса и многие другие.
Последние комментарии | Перейти к обсуждению на форуме трейдеров (8)
knyazeff.vad
Будет ли добавлена в  MQL 5 функция типа  push_back() и описание работы со строковыми  функциями в векторах?
Mikhail Mishanin
Приветствую, просьба дополнить Справочник MQL5 примерами и пр. по матрицам и векторам, что их можно передать по ссылке matrix& и т.п.
Mikhail Mishanin
Добрался до внешнего ввода/вывода, файловые операции на вектора/матрицы планируются? FileWrite/ReadMatrix будет?
Aliaksandr Hryshyn
Mikhail Mishanin #:
Добрался до внешнего ввода/вывода, файловые операции на вектора/матрицы планируются? FileWrite/ReadMatrix будет?
FileWriteStruct не работает?
Mikhail Mishanin
Aliaksandr Hryshyn #:
FileWriteStruct не работает?

пока без попыток, вопрос просто как будут записываться/читаться векторстолбцы и векторстроки, ну и матрицы конечно.

сегодня доберусь до кодинга, сообщу результат.

Графика в библиотеке DoEasy (Часть 96): Работа с событиями мышки и графика в объектах-формах Графика в библиотеке DoEasy (Часть 96): Работа с событиями мышки и графика в объектах-формах
В статье начнём разработку функционала для работы с событиями мышки в объектах-формах и добавим новые свойства и их отслеживание в объект-символ. Помимо этого сегодня доработаем класс объекта-символа, так как с момента его написания у символов графика появились новые свойства, которые желательно учитывать и отслеживать их изменение.
Веб-проекты (Часть I): Создание веб-приложения в схеме Laravel/Nuxt/MetaTrader 5 Веб-проекты (Часть I): Создание веб-приложения в схеме Laravel/Nuxt/MetaTrader 5
Разработчики MetaTrader 5 предоставили MQL-сообществу множество технологических решений, что даёт возможность реализовывать сложные программные комплексы, схемы которых могут выходить даже за рамки «песочницы» локального компьютера.
Веб-проекты (Часть II): Система авторизации Laravel/Nuxt Веб-проекты (Часть II): Система авторизации Laravel/Nuxt
В этой статье создадим систему авторизации через браузерное приложение и через торговый терминал MetaTrader 5. Можно будет зарегистрироваться в системе, указав свои учётные данные.
Графика в библиотеке DoEasy (Часть 95): Элементы управления составными графическими объектами Графика в библиотеке DoEasy (Часть 95): Элементы управления составными графическими объектами
В статье рассмотрим инструментарий для управления составными графическими объектами - элементы управления расширенным стандартным графическим объектом. Сегодня мы немного отступим от темы перемещения составного графического объекта и сделаем обработчик события изменения графика, на котором находится составной графический объект, и займёмся объектами управления составным графическим объектом.