Матрицы и векторы в MQL5
Для работы с упорядоченными однотипными данными обычно используются массивы, которые позволяет обращаться к каждому элементу по индексу. Массивы широко используются при решении множества задач из линейной алгебры, математического моделирования, в машинном обучении и т.д. Решение этих задач, в общем виде, основывается на математических операциях с использованием матриц и векторов, которые позволяют компактно записывать в виде простой формулы очень сложные преобразования. Для программирования таких операций нужен не только хороший уровень знания математики, но и умение писать сложные вложенные циклы. Отладка и поиск ошибок в таких программах могут быть очень изнуряющими.
Специальные типы данных 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) | Создает матрицу с единицами по указанной диагонали и нулями в остальных местах | |
void matrix.Identity() | Заполняет матрицу единицами на главной диагонали и нулями в остальных местах | |
void static matrix.Ones(const int rows, const int cols) | Создает новую матрицу по числу строк и столбцов, заполненную единицами | |
void static matrix.Zeros(const int rows, const int cols) | Создает новую матрицу по числу строк и столбцов, заполненную нулями | |
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) | Создает новую матрицу по числу строк и столбцов, заполненную скалярным значением | |
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-декомпозицию | |
bool matrix.Eig(matrix& eigen_vectors, vector& eigen_values) | Вычисляет собственные значения и правые собственные векторы квадратной матрицы | |
bool matrix.EigVals(vector& eigen_values) | Вычисляет собственные значения общей матрицы | |
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
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 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Добрался до внешнего ввода/вывода, файловые операции на вектора/матрицы планируются? FileWrite/ReadMatrix будет?
FileWriteStruct не работает?
пока без попыток, вопрос просто как будут записываться/читаться векторстолбцы и векторстроки, ну и матрицы конечно.
сегодня доберусь до кодинга, сообщу результат.