MQL5 中的矩阵和向量

MetaQuotes | 3 三月, 2022

有序数据的集合,其中所有元素的类型相同,通常会经由数组进行操作,其内每个元素都可以通过按照其索引进行访问。 数组广泛用于解决各种线性代数问题、数学建模任务、机器学习、等等。 一般来说,这类问题的解决方案是基于矩阵和向量的数学运算,运用这些矩阵和向量,可以将非常复杂的变换改写成紧凑、简单的方程形式。 此类运算的程序化实现需要优良的数学知识,以及编写复杂嵌套循环的能力。 针对这样的程序进行调试和修复故障可能具有相当的挑战性。 

运用特殊的数据类型“矩阵”和“向量”,可以创建非常贴合数学符号本意的代码,同时避免创建嵌套循环,或在计算中分心记忆数组的正确索引。 在本文中,我们将了解如何在 MQL5 中创建、初始化和运用矩阵向量对象。


“向量(vector)”类型

向量(vector)是一个一维的双精度型(double)数组。 针对向量定义了以下运算:加法和乘法,以及获取向量长度或模(module)的范数(Norm)。 在程序化过程中,向量通常由同质元素的数组来表示,针对这些不能定义非规则向量运算,即数组不能相加或相乘,并且它们没有范数。

在数学中,向量可以表示为行向量,即由一行和 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
创建指定长度的向量,并用指定值填充
operator =

返回向量的副本
void vector.Resize(const vector v);
  往结尾添加新值来调整向量大小 


矢量创建示例:

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]

*/ 

Init() 方法不仅可用于为向量分配内存,还可依据函数值来初始化向量元素。 在这种情况下,向量大小作为第一个参数传递给 Init,函数名作为第二个参数。 如果函数包含参数,则应在函数名后立即指定这些参数,并用逗号分隔。

函数本身必须包含向量的引用,且作为第一个参数传入。 在 Init 调用期间不应传递向量。 我们以 Arange 函数为例来查看方法的运算。 此函数模仿 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

*/

Arange 函数有两个可选参数,“start” 和 “stop”。 因此,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("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]

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

向量支持两个大小相同向量的元素加法、减法、乘法和除法运算。

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]

*/

为这些数据类型定义了四种运算。

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

*/

如您从示例中所见,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);   
 }
/*
Execution result

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

构造一个矩阵,在指定的对角线上填 1,在其它地方填 0

void matrix.Identity()

identity

在矩阵主对角线上填 1,并在其它地方填 0

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

ones

根据行数和列数构造一个新的矩阵,并用 1 填充

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

zeros

根据行数和列数构造一个新的矩阵,并用 0 填充

void static matrix.Tri(const int rows, const int cols, const int ndiag=0)
tri  构造一个矩阵,在指定对角线上填 1,在其下和其它地方填 0 
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()); // 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]]

*/

以下示例展示了如何使用自定义函数填充矩阵:

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

*/ 

矩阵可以通过两种方式来构造,且无需初始化值: 

//--- create a matrix of a given 'rows x cols' size
  matrix m(3, 3);

// ------ equivalent
  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);
//--- 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

*/


矩阵和向量运算

矩阵提供了解决数学问题的特殊方法:

矩阵分解:

方法

NumPy 中的类比法

说明
bool matrix.Cholesky(matrix& L) cholesky 计算 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]=C[M,K],即左侧矩阵中的列数必须等于右侧矩阵中的行数。 如果维度不一致,则结果为空矩阵。 我们通过示例查看所有矩阵相乘的变体。

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

*/

为了更好地理解矩阵和向量类型是如何排列的,这些示例展示了如何使用矩阵代替向量。 这意味着向量可以表示为矩阵。


“complex(复数)”类型的复数

有些数学问题需要使用新的数据类型“complex numbers(复数)”。 complex(复数)类型是一种结构:
struct complex
  {
   double             real;   // real part
   double             imag;   // imaginary part
  };
“complex” 类型可以作为 MQL5 函数的参数按值传递(与普通结构不同,普通结构只能通过引用传递)。 对于从 DLL 导入的函数,“complex” 类型只能通过引用传递。

后缀 “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 number
复数只能进行简单的运算: =, +, -, *, /, +=, -=, *=, /=, ==, !=。

不久之后将增加对其它数学函数的支持,从而能够计算绝对值、正弦、余弦、等等