preview
Fatorando Matrizes — O Básico

Fatorando Matrizes — O Básico

MetaTrader 5Exemplos | 13 março 2024, 09:34
393 0
Daniel Jose
Daniel Jose

Introdução

Olá pessoal, e sejam bem-vindos a mais um de meus artigos, voltado a ter um conteúdo didático.

Neste artigo falaremos sobre cálculos matriciais. Antes que você, meu caro leitor, desista de se quer ler o artigo. Imaginando que vamos falar de algo extremamente complicado e que só existe para sacanear estudantes em aulas de matemática. Quero que você entenda uma coisa. Diferente do que muitos dizem, pregam e afirmam. Um bom programador, não é aquele que escreve um programa gigantesco e que somente ele consegue entender. Ou aquele, que programa a linguagem da moda. O verdadeiro e bom programador, é aquele que entende que um computador, nada mais é do que uma calculadora, que podemos dizer como o cálculo deverá ser feito. Não importa se você está criando isto ou aquilo. Pode ser um simples editor de texto, que aparentemente nada tem de matemática sendo colocada ali. Dentro do código. Mas isto é uma ilusão. Mesmo um editor de texto, tem em seu código bastante matemática. Ainda mais se ele contiver um corretor ortográfico.

Basicamente existem duas maneiras de efetuarmos fatorações, ou cálculos, se assim desejar chamar, em programação. A primeira maneira é fazendo, ou escrevendo, o cálculo de forma escalar. Ou seja, você usando funções que a linguagem lhe fornece, escreve a fórmula, da maneira como ela se apresenta. Seria algo do tipo mostrado abaixo:


Esta fórmula que todos reconhecem, pois nos permite calcular as raízes de uma função quadrática. Pode ser facilmente escrita em qualquer linguagem de programação, usando as funções e recursos presentes na linguagem. Mas também existe uma outra maneira de se fazer cálculos. E esta maneira é a matricial, ou vetorial, como alguns gostam de dizer. Bem, mas chamarei neste artigo de matricial, devido ao fato de usarmos matriz para representar as coisas. Este tipo de fatoração, normalmente tem uma aparecia mostrada abaixo.


Alguns entusiastas, que muitas vezes estão iniciando na área da programação. Não fazem a mínima ideia de como escrever isto em forma de código. Então acabam traduzindo esta notação matricial em uma notação escalar de maneira a conseguir obter o mesmo resultado.

A questão não é, se isto é ou não certo. Se o resultado obtido é o correto, pouco importa. No caso esta matriz mostrada acima, efetua exatamente a rotação de alguma coisa em torno de algum ponto indicado. O que eu quero, neste artigo mostrar a você, meu caro e entusiasta leitor. É como você pode, usando o MQL5, ou qualquer outra linguagem, conseguir escrever esta forma matricial, direto no seu código. Sem precisar de fato, converter ela em uma forma escalar. Fazer este tipo de coisa não é algo complicado, como muitos pensam. Na verdade é bem mais simples do que parece. Então me acompanhem neste artigo, para aprender como fazer isto.


Por que usar matrizes e não a forma escalar

Antes de começarmos a ver como fazer a codificação. Vamos entender, como escolher uma ou outra forma de implementar a fatoração.

Obviamente, qualquer coisa que você procure sobre uma linguagem, irá com toda a certeza de deparar com a maneira escalar de escrita do código. Mas por que ?!?! O motivo é que o uso da forma matricial é um pouco mais confusa de se escrever em termos de código. Para entender isto, tente escrever a matriz mostrada anteriormente, ou qualquer outra matriz, como sendo linhas de código. Você notará que é uma tarefa um tanto quanto desajeitada de ser feita. O código fica com uma aparência meio estranha, não fazendo muito sentido. Porém da maneira escalar, o código será bem mais simples de ser compreendido. Por isto, que você não vê de fato ninguém codificando fatorações em forma matricial.

Porém, diversas fatorações, são muito mais simples de codificar de maneira matricial, do que o equivalente em escalar. Um exemplo disto é se você precisar, manipular quantidades enormes de dados, que podem ser facilmente expressas em um array multidimensional. Para entender isto, vamos pensar primeiramente em uma imagem vetorial. Que no lugar de pintar a tela pixel a pixel. Usamos vetores. Isto nos permite rotacionar, mudar a escala, fazer uma deformação por cisalhamento, entre diversas outras manipulações. Isto de forma extremamente simples, desde que você faça uso de matrizes. Ou melhor dizendo, codifique os cálculos para usarem matrizes. Você conseguirá fazer as mesmas transformações de maneira escalar. Porém usando matrizes será muito, mas muito mais simples.

Mas se uma imagem vetorial, não lhe parece complicada o suficiente. Pense em objetos 3D. Talvez agora você consiga entender, por que fatorações usando matrizes são tão interessantes.

Em um objeto 3D, qualquer transformação, é muito mais fácil de ser feita, usando matrizes. Fazer a mesma coisa de forma escalar é algo, extremamente complicado e trabalhoso. Até dá para fazer, mas é tão mais complicado, que chega a ser desanimador. Pense no fato de você precisar fazer uma projeção ortogonal, de um objeto 3D, usando fórmulas como vista no início do artigo. Seria um pesadelo programar tal coisa. Porém criando um código, que fará uso de matrizes. Pouco importa a complexidade do objeto 3D. Fazer a projeção ortogonal é muito simples e fácil.

E é justamente isto que é usado em placas gráficas e programas de modelagem 3D. Eles basicamente usam matrizes em seus cálculos. Mesmo que você não perceba tal coisa. Mas então, quando usar uma e quando usar a outra forma de codificar as fatorações ?!?! Bem, isto vai depender de sua experiência e conhecimento. Não existe uma regra rígida, que lhe force a usar um ou outro método. Mas saber como programar ambos os métodos é importante, para que você consiga fazer as coisas da forma mais rápida, simples e eficaz quanto for possível fazer.

Como a forma escalar, ou seja, programando literalmente a fatoração a ser executada. É extremamente difundida e mostrada. Aqui vamos focar apenas na maneira de codificar a forma matricial. Para separar as coisas e tornar mais simples o entendimento, vamos dividir isto em tópicos bem específicos. Começando agora.


A forma escalar de fazer as coisas

Aqui quero mostrar algo bem simples e de fácil compreensão. Lembre-se este artigo, tem como finalidade ser didático. Ele não visa programar algo específico.

Para começar vamos criar um pequeno programa em MQL5. O que irei mostrar aqui, pode ser aplicado em qualquer linguagem que você queira usar. Tudo depende do qual familiarizado você esteja com a linguagem. Vamos começar com um código bem simples, como mostrado abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property indicator_chart_window
04. #property indicator_plots 0
05. //+------------------------------------------------------------------+
06. #include <Canvas\Canvas.mqh>
07. //+------------------------------------------------------------------+
08. CCanvas canvas;
09. //+------------------------------------------------------------------+
10. int OnInit()
11. {
12.     
13.     int px, py;
14.     
15.     px = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
16.     py = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
17. 
18.     canvas.CreateBitmapLabel("BL", 0, 0, px, py, COLOR_FORMAT_ARGB_NORMALIZE);
19.     canvas.Erase(ColorToARGB(clrWhite, 255));
20.         
21.     canvas.Update(true);
22.     
23.     return INIT_SUCCEEDED;
24. }
25. //+------------------------------------------------------------------+
26. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
27. {
28.     return rates_total;
29. }
30. //+------------------------------------------------------------------+
31. void OnDeinit(const int reason)
32. {
33.     canvas.Destroy();
34. }
35. //+------------------------------------------------------------------+

Este código, como você pode notar é um indicador. Poderíamos usar qualquer outro, mas um indicador ao meu ver será mais interessante, devido a algumas coisas que podemos fazer. E o fato de ele poder interceptar determinadas chamadas oriundas do MetaTrader 5.

Bem, este código é bem simples, e não faz praticamente nada. Ele apenas torna a janela do gráfico branco, ou da cor que você desejar usar. Observem que para simplificar ao máximo o código que será criado, na linha seis estou usando a biblioteca padrão do MQL5. Ou seja, nesta linha estou dizendo para o compilador, adicionar o arquivo de cabeçalho Canvas.mqh, que é criado por padrão, durante a instalação do MetaTrader 5. Isto simplificará bastante o que será necessário ser explicado neste artigo. Depois disto, na linha oito, declaramos uma variável global, a fim de nos dar acesso a classe CCanvas. Ok, como se trata de uma classe, precisamos usar ela, de uma determinada maneira. Um detalhe aqui. Normalmente eu gosto de usar classes como ponteiros. Mas para simplificar ao máximo, já que muitos poderiam ficar em dúvida sobre por que estou fazendo isto, ou aquilo. Vou usar a classe como todos normalmente a usam. Ou seja, uma variável simples que nos permite referenciar algo.

Muito bem, nas linhas 15 e 16, capturam o tamanho da janela do gráfico onde o indicador está sendo colocado. Observe que não estamos criando uma janela para ele. Isto é informado na linha três. E como o indicador não irá plotar absolutamente nada, evitamos que o compilador fique nos gerando mensagens de alerta. Para fazer isto, usamos a linha quatro. Então se você aplicar, este indicador em uma sub janela, as informações capturadas nas linhas 15 e 16 serão diferentes.

Na linha 18, dizemos a classe CCanvas, que desejamos que ela crie, uma região onde iremos pintar, ou plotar algo. Esta região vai do canto superior esquerdo, isto devido ao segundo e terceiro parâmetros, até o canto inferior esquerdo. Isto devido aos dois próximos parâmetros. Já o primeiro parâmetro é o nome que o objeto criado pela classe CCanvas terá. E o último parâmetro diz que tipo de combinação será usada para pintar na região. Consulte a documentação para mais detalhes sobre estes modos de pintura. No caso aqui, vamos usar o modelo que temos um alpha na cor, ou seja, podemos criar uma transparência no que estaremos pintando na região selecionada.

A próxima coisa a ser feita é limpar a região. Isto é feito na linha 19 e logo depois na linha 21, dizemos para que a informação presente na memória, seja jogada na tela. É importante, que você entenda isto. A informação que você colocar na região, usando CCanvas, não irá aparecer na tela no mesmo momento que você a estiver criando. Toda a pintura é feita primeiro na memória. Somente quando a linha 21 é executada, é que ela é mostrada na tela. Simples assim. Agora que já temos a base inicial. Vamos ver o que pintaremos na tela. Isto é mostrado no código abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property indicator_chart_window
04. #property indicator_plots 0
05. //+------------------------------------------------------------------+
06. #include <Canvas\Canvas.mqh>
07. //+------------------------------------------------------------------+
08. CCanvas canvas;
09. //+------------------------------------------------------------------+
10. #define _ToRadians(A) (A * (M_PI / 180.0))
11. //+------------------------------------------------------------------+
12. void Arrow(const int x, const int y, const ushort angle)
13. {
14.     int ax[] = {0, 150, 100, 150},
15.         ay[] = {0, -75,   0,  75};
16.     int dx[ax.Size()], dy[ax.Size()];
17.     
18.     for (int c = 0; c < (int)ax.Size(); c++)
19.     {
20.         dx[c] = (int)((ax[c] * cos(_ToRadians(angle))) + (ay[c] * sin(_ToRadians(angle)))) + x;
21.         dy[c] = (int)((ax[c] * (-sin(_ToRadians(angle)))) + (ay[c] * cos(_ToRadians(angle)))) + y;
22.     }
23. 
24.     canvas.FillPolygon(dx, dy, ColorToARGB(clrBlue, 255));
25.     canvas.FillCircle(x, y, 5, ColorToARGB(clrRed, 255));
26. }
27. //+------------------------------------------------------------------+
28. int OnInit()
29. {    
30.     int px, py;
31.     
32.     px = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
33.     py = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
34. 
35.     canvas.CreateBitmapLabel("BL", 0, 0, px, py, COLOR_FORMAT_ARGB_NORMALIZE);
36.     canvas.Erase(ColorToARGB(clrWhite, 255));
37.         
38.     Arrow(px / 2, py / 2, 60);
39. 
40.     canvas.Update(true);
41.     
42.     return INIT_SUCCEEDED;
43. }
44. //+------------------------------------------------------------------+
45. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
46. {
47.     return rates_total;
48. }
49. //+------------------------------------------------------------------+
50. void OnDeinit(const int reason)
51. {
52.     canvas.Destroy();
53. }
54. //+------------------------------------------------------------------+

Note que aqui, estamos fazendo a tradução em forma escalar, da fórmula que é mostrada no ínício do artigo. Onde temos uma notação matricial sendo feita. Ou seja, faremos a rotação de um objeto. Mas qual objeto ?!?! Para começar a entender, olhe a imagem abaixo. Ela é o que será visto quando o código foi executado no MetaTrader 5.


Ou seja, o objeto que estamos rotacionando é uma seta. Mas olhando este código, você consegue ver onde está sendo desenhada a tal seta ?!?! Bem, você logo nota que na linha 38, temos uma chamada para um procedimento chamado ARROW, ou seja, SETA. E este procedimento está na linha 12. Então obviamente a seta deve estar em algum ponto dentro do procedimento da linha 12. Mas onde ?!?! Bem, a seta, se encontra definida nas linhas 14 e 15. Observe que temos dois arrays para desenhar a seta. Aqui, estamos desenhando ela de forma bem simples. Agora começa a parte divertida.

Esta seta está sendo definida em termos absolutos. Ou seja, estamos desenhando ela como ela seria representada no mundo real. Mas também poderíamos estar representando ela de maneira virtual. Ou melhor dizendo. No lugar de usar valores inteiros, poderíamos usar valores do tipo flutuante. Assim poderíamos ao multiplicar os valores que desenha a seta, por um escalar qualquer, dizer qual o tamanho da seta. Porém, para não confundir e complicar as coisas, não fiz isto aqui. Quero deixar a coisa o mais simples possível. Para que você, meu caro leitor, consiga entender como implementar uma fatoração matricial.

Ok. Na linha 16, temos algo que pode ser confuso, para muitos. Mas é simples de entender o motivo de ter sido escrito desta maneira. Como não sabemos de antemão, quantos pontos será usado para desenhar o objeto, e precisamos de um array intermediário, para que na linha 24 possamos desenhar o mesmo. Precisamos de alguma forma, dizer para a aplicação, para alocar memória para nos. Ao usarmos a coisa como é vista na linha 16, dizemos ao compilador, para alocar a mesma quantidade de espaço, que foi preciso para desenhar os pontos. Ou seja, temos um array dinâmico, sendo usado, porém que é alocado de forma estática. Se não usássemos esta forma de codificar, iriamos ter que fazer o mesmo código visto no procedimento Arrow, ser montado como mostrado no fragmento abaixo.

11. //+------------------------------------------------------------------+
12. void Arrow(const int x, const int y, const ushort angle)
13. {
14.     int ax[] = {0, 150, 100, 150},
15.         ay[] = {0, -75,   0,  75};
16.     int dx[], dy[];
17.     
18.     ArrayResize(dx, ax.Size());
19.     ArrayResize(dy, ax.Size());
20.     
21.     for (int c = 0; c < (int)ax.Size(); c++)
22.     {
23.         dx[c] = (int)((ax[c] * cos(_ToRadians(angle))) + (ay[c] * sin(_ToRadians(angle)))) + x;
24.         dy[c] = (int)((ax[c] * (-sin(_ToRadians(angle)))) + (ay[c] * cos(_ToRadians(angle)))) + y;
25.     }
26. 
27.     canvas.FillPolygon(dx, dy, ColorToARGB(clrBlue, 255));
28.     canvas.FillCircle(x, y, 5, ColorToARGB(clrRed, 255));
29. 
30.     ArrayFree(dx);
31.     ArrayFree(dy);
32. }
33. //+------------------------------------------------------------------+

Note que a linha 16, agora temos uma indicação de array dinâmico. Como este tipo de array, tem o espaço que será usado, sendo definido em RUN-TIME, ou tempo de execução. Precisamos fazer as seguintes mudanças. Nas linhas 18 e 19, do fragmento, precisamos alocar o espaço necessário, para que o programa não quebre durante a execução. E ainda no fragmento, precisamos nas linhas 30 e 31, devolver a memória alocada para o sistema operacional. Isto é uma boa prática em programação. Muitos programadores, costumam não devolver recursos alocados. E isto vai tornando a aplicação, um verdadeiro come recurso. Já que ela aloca o recurso e não libera ele. Sendo desta forma uma aplicação mal educada. Mas tirando estas pequenas diferenças, que você pode ver entre o fragmento e o código principal. Todo o restante funciona da mesma maneira.

Muito bem, então voltemos ao código principal. Observe que o laço na linha 18, irá varrer todos os pontos que fazem parte da figura que estamos querendo, no caso, rotacionar.

Agora preste atenção a uma coisa. Estamos tanto rotacionando a figura, como também a movendo para um determinado local. O local em questão são os pontos X e Y. Que no caso é o centro da tela. Veja na linha 38, onde definimos o local. Assim como também definimos o ângulo de giro. No caso 60 graus.

Como as funções trigonométricas, por padrão, e isto em todas as linguagens de programação que conheço. Usam valores em radianos e não em graus, precisamos assim, executar a conversão, de graus para radianos. É justamente isto que é feito, pela definição da linha dez. Desta maneira, fica mais natural, dizermos quanto uma figura deverá girar, do que ter de expressar o valor em radianos. O que em muitos momentos pode ser bastante confuso de ser feito.

Assim, pegamos cada um dos pontos no array, e aplicamos nele, exatamente a fórmula mostrada abaixo.


Isto fará com que a imagem gire no sentido anti-horário, sendo que zero graus, seria nove horas. Ou para que você entenda melhor, 90 graus seria seis horas, 180 graus, seria três horas e 270 graus zero horas. Lembre-se rotação no sentido anti-horário. Para mudar este comportamento, você precisaria mexer na forma como o cálculo é feito. Não é algo complicando, na verdade é bem simples de ser feito. No caso para girar no sentido horário, a fórmula teria que ser mudada para a vista logo abaixo.


Veja que é bem simples, de ser feito. Neste caso três horas seria o ângulo de zero graus, seis horas o ângulo de 90 graus, nove horas o ângulo de 180 graus e 270 graus seria zero horas. Apesar de parecer termos mudando apenas o valor do ângulo de zero com o de 180 graus. Fizemos mais do que isto. Agora o sentido de giro, mudou de anti-horário para horário.

Mas não é bem isto que quero mostrar. Porém isto que foi feito, serve apenas como curiosidade. Para de fato, explicar o que queremos mostrar, primeiro precisamos apresentar a forma como todos, normalmente fazem as coisas. Justamente por ser mais fácil e mais simples em diversos casos. Porém, não é algo que conseguimos fazer com que se adapte facilmente a qualquer situação.

Mas não vou entrar em detalhes sobre isto, neste momento. Já que o foco do artigo não é este. E sim como modelar cálculos matriciais. Assim sendo, vamos ver como normalmente os programadores lidam com tais questões. A maneira mais simples, é transformar as coisas em algo que seriam unidades virtuais. Fazemos todos os cálculos, e depois transformar o resultado em unidades gráficas de tela. Criar algo usando unidades virtuais, significa que não usaremos valores inteiros, e sim valores flutuantes, normalmente do tipo double. Assim o mesmo código visto antes fica como mostrado abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property indicator_chart_window
04. #property indicator_plots 0
05. //+------------------------------------------------------------------+
06. #include <Canvas\Canvas.mqh>
07. //+------------------------------------------------------------------+
08. CCanvas canvas;
09. //+------------------------------------------------------------------+
10. #define _ToRadians(A) (A * (M_PI / 180.0))
11. //+------------------------------------------------------------------+
12. void Arrow(const int x, const int y, const ushort angle, const uchar size = 100)
13. {
14.     double ax[] = {0.0,  1.5, 1.0, 1.5},
15.            ay[] = {0.0, -.75, 0.0, .75};
16.     int dx[ax.Size()], dy[ax.Size()];
17.     
18.     for (int c = 0; c < (int)ax.Size(); c++)
19.     {
20.         ax[c] *= size;
21.         ay[c] *= size;
22.         dx[c] = (int) ((ax[c] * cos(_ToRadians(angle))) + (ay[c] * sin(_ToRadians(angle)))) + x;
23.         dy[c] = (int) ((ax[c] *(-sin(_ToRadians(angle)))) + (ay[c] * cos(_ToRadians(angle)))) + y;
24.     }
25. 
26.     canvas.FillPolygon(dx, dy, ColorToARGB(clrGreen, 255));
27.     canvas.FillCircle(x, y, 5, ColorToARGB(clrRed, 255));
28. }
29. //+------------------------------------------------------------------+
30. int OnInit()
31. {    
32.     int px, py;
33.     
34.     px = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
35.     py = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
36. 
37.     canvas.CreateBitmapLabel("BL", 0, 0, px, py, COLOR_FORMAT_ARGB_NORMALIZE);
38.     canvas.Erase(ColorToARGB(clrWhite, 255));
39.         
40.     Arrow(px / 2, py / 2, 60);
41. 
42.     canvas.Update(true);
43.     
44.     return INIT_SUCCEEDED;
45. }
46. //+------------------------------------------------------------------+
47. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
48. {
49.     return rates_total;
50. }
51. //+------------------------------------------------------------------+
52. void OnDeinit(const int reason)
53. {
54.     canvas.Destroy();
55. }
56. //+------------------------------------------------------------------+

Observe que quase nada mudou, salvo o fato de que agora podemos dar zoom no objeto criado. Isto é feito nas linhas 20 e 21. Já o objeto, está sendo criado nas linhas 14 e 15. Todo o código permaneceu basicamente idêntico. Porém, esta simples mudança feita, já ajuda muito a portar a aplicação para outros cenários. Como por exemplo, se o usuário mudar as dimensões do gráfico, você pode ajustado, única e exclusivamente o parâmetro size na linha 12. Manter os objetos dentro de uma dada proporção.

Mas ainda não entendi como isto me ajudaria a trabalhar com matrizes. Se posso fazer assim, por que tentar complicar ?!?! Bem, vamos ver se realmente seria mais complicado fazer este tipo de coisa usando matrizes. Para isto vamos a um novo tópico.


Agora usando fatoração por matriz

Como o intuito aqui é ser didático. Vou tentar manter a coisa no seu nível mais simples quanto for possível ser feito. Assim sendo, iremos implementar apenas e somente o que será preciso, para demonstrar o que precisamos. Ou seja, a multiplicação de matrizes, neste caso específico que estamos mostrando. Mas você, meu caro leitor, verá que mesmo fazendo apenas isto, ainda assim poderemos e teremos, o suficiente para efetuar a multiplicação de uma matriz por um escalar. A grande dificuldade que muita gente tem em implementar um código usando fatoração de matrizes, é que diferente de uma fatoração escalar, onde em quase todos os casos a ordem dos fatores não altera o resultado. Quando se usa matrizes, a coisa não é bem assim. Mas então como seria o mesmo código visto, no tópico anterior, só que usando matrizes ?!?! Bem, não se assuste meu caro leitor. Mas o mesmo código visto anteriormente, é implementado como mostrado abaixo. Isto para que a fatoração seja feita usando matrizes.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property indicator_chart_window
04. #property indicator_plots 0
05. //+------------------------------------------------------------------+
06. #include <Canvas\Canvas.mqh>
07. //+------------------------------------------------------------------+
08. CCanvas canvas;
09. //+------------------------------------------------------------------+
10. #define _ToRadians(A) (A * (M_PI / 180.0))
11. //+------------------------------------------------------------------+
12. void MatrixA_x_MatrixB(const double &A[][], const double &B[][], double &R[][], const int nDim)
13. {
14.     for (int c = 0, size = (int)(B.Size() / nDim); c < size; c++)
15.     {
16.         R[c][0] = (A[0][0] * B[c][0]) + (A[0][1] * B[c][1]);
17.         R[c][1] = (A[1][0] * B[c][0]) + (A[1][1] * B[c][1]);
18.     }
19. }
20. //+------------------------------------------------------------------+
21. void Arrow(const int x, const int y, const ushort angle, const uchar size = 100)
22. {
23.     double M_1[2][2]{
24.                         cos(_ToRadians(angle)), sin(_ToRadians(angle)),
25.                         -sin(_ToRadians(angle)), cos(_ToRadians(angle))
26.                     },
27.            M_2[][2] {
28.                         0.0,  0.0,
29.                         1.5, -.75,
30.                         1.0,  0.0,
31.                         1.5,  .75
32.                     },
33.            M_3[M_2.Size() / 2][2];
34. 
35.     int dx[M_2.Size() / 2], dy[M_2.Size() / 2];
36.     
37.     MatrixA_x_MatrixB(M_1, M_2, M_3, 2);
38.     ZeroMemory(M_1);
39.     M_1[0][0] = M_1[1][1] = size;
40.     MatrixA_x_MatrixB(M_1, M_3, M_2, 2);
41. 
42.     for (int c = 0; c < (int)M_2.Size() / 2; c++)
43.     {
44.         dx[c] = x + (int) M_2[c][0];
45.         dy[c] = y + (int) M_2[c][1];
46.     }
47. 
48.     canvas.FillPolygon(dx, dy, ColorToARGB(clrPurple, 255));
49.     canvas.FillCircle(x, y, 5, ColorToARGB(clrRed, 255));
50. }
51. //+------------------------------------------------------------------+
52. int OnInit()
53. {    
54.     int px, py;
55.     
56.     px = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
57.     py = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
58. 
59.     canvas.CreateBitmapLabel("BL", 0, 0, px, py, COLOR_FORMAT_ARGB_NORMALIZE);
60.     canvas.Erase(ColorToARGB(clrWhite, 255));
61.         
62.     Arrow(px / 2, py / 2, 160);
63. 
64.     canvas.Update(true);
65.     
66.     return INIT_SUCCEEDED;
67. }
68. //+------------------------------------------------------------------+
69. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
70. {
71.     return rates_total;
72. }
73. //+------------------------------------------------------------------+
74. void OnDeinit(const int reason)
75. {
76.     canvas.Destroy();
77. }
78. //+------------------------------------------------------------------+

Deixei o código o mais simples possível, sem muitos ajustes a serem feitos, para que você, meu caro e entusiasta leitor, possa conseguir entender ele. Normalmente quando escrevo uma fatoração que será feita usando cálculos matriciais, faço isto de uma forma um pouco diferente. Mas esta forma será vista em um próximo artigo. Já que será necessário explicar alguns detalhes e cuidados a serem tomados. Assim devo fazer um outro, apenas para explicar como estes destalhes, pois apesar de parecer simples, se você não tomar os devidos cuidados, acabará em um buraco sem fundo. Bem, mas o que este código maluco, está fazendo ?!?! A resposta: Ele está fazendo a mesma coisa que era feita no tópico anterior. Só que aqui estamos usando a multiplicação de matrizes para fazer os mesmos cálculos. Talvez olhando este código você o ache muito complicado e difícil de entender. Mas não, ele é bem mais simples do que o anterior. Mas muito mais simples mesmo. Desde que você entenda como multiplicar uma matriz por outra. Apesar de tudo aqui, a multiplicação não está sendo feita da forma correta. Ou melhor dizendo, não nos permitirá fazer algumas coisas, se for necessários modificar o código. Mas como eu disse, esta versão é apenas para demonstra. A versão correta, será vista no próximo artigo. No entanto, a fatoração terá o resultado esperado, mesmo que ela esteja sendo feita de forma um tanto estranha.

Este é um detalhe do qual você precisa saber, antes mesmo de querer usar esta mesma modelagem, em quais quer atividades. Mas se você já entende minimamente bem, como as matrizes são fatoradas. O resto é bem mais simples de ser entendido. Observe que na linha 12, fazemos a implementação do código responsável por efetuar a fatoração matricial. Coisa simples. Basicamente multiplicamos a primeira matriz pela segunda, e colocamos o resultado em uma terceira. Como a multiplicação se dá colunas de uma matriz, pelas linhas da outra. Temos a fatoração sendo feita de maneira bastante simples. Aqui temos que tomar alguns cuidados como o fato do número de linhas de uma matriz ser igual ao número de colunas da outra. Isto é matemática básica sobre matrizes, então procure entender isto antes, caso você não sabia como proceder, ou por que de ser feito assim. Na matriz A colocamos as colunas e na matriz B as linhas. Assim no nosso caso teríamos algo equivalente ao que é visto abaixo.

Sei que olhando isto pela primeira vez, pode parecer muito confuso. Mas é muito simples. A matriz com elementos A será multiplicada pela matriz com elementos B. Isto resultará na matriz de elementos R. Basicamente é assim:
R11 = (A11 x B11) + (A12 x B12);  R12 = (A21 x B11) + (A22 x B12) R21 = (A11 x B21) + (A12 x B22);  R22 = (A21 x B21) + (A22 x B22) R31 = (A11 x B31) + (A12 x B32);  R32 = (A21 x B31) + (A22 x B32)
e dai por diante. Note que é exatamente como seria feito se o cálculo fosse montado de forma escalar. É justamente isto que o procedimento da linha 12 está fazendo. Agora veja como no momento de construirmos os cálculos, temos uma liberdade muito maior. Para perceber isto, vamos ao procedimento onde a seta é construída e apresentada. Ou seja, na linha 21. Observe que aqui temos 5 matrizes sendo definidas. Poderíamos usar menos. Mas vamos primeiro entender isto, No futuro, quem sabe, mostrarei como poderíamos modelar isto de uma forma bem mais amigável e de compreensão bem mais simples. Mas mesmo neste modelo, ainda sim é bem simples de entender. Entre as linhas 23 e 33, desenhamos as nossas matrizes. Estas serão usadas nos cálculos. Agora na linha 35, declaramos as que usaremos para apresentar a seta na tela. E entre as linhas 37 e 40, efetuamos os cálculos desejados. Agora preste atenção, pois o que será feito é algo bem interessante. Isto pelo ponto de vista prático e em termos de liberdade na forma de fatorar as coisas. Na linha 37, primeiramente multiplicamos a matriz M_1 pela matriz M_2, resultado vai para a matriz M_3. O que esta multiplicação está fazendo ?!?! Bem, ela está girando a imagem. Porém não podemos apresentar ela ainda na tela, isto por que a escala, ainda é a usada na modelagem encontrada na matriz M_2. Para que possamos apresentar a imagem na escala correta, precisamos de mais um cálculo. Mas antes na linha 38, limpamos o conteúdo da matriz M_1, e logo depois na linha 39, dizemos como a imagem terá sua escala modificada. Neste ponto, temos diversas novas possibilidades. Mas vamos deixar apenas a mudança de escala. Já na linha 40, multiplicamos os dados do objeto, que já se encontra rotacionado, pela nova matriz. Esta que já contem a escala a ser usada. A representação disto, pode ser visto na imagem abaixo.

Aqui Sx é a nova dimensão no eixo X, e Sy é a nova dimensão no eixo Y. Como estamos colocando ambos valores como sendo Size. A mudança será como se estivéssemos dando um zoom no objeto. Por isto, que eu disse que podemos fazer bem mais coisas. Muito bem, agora como o procedimento FillPolygon, não sabe lidar com esta estrutura em forma de matriz. Precisamos separar os dados, de maneira que FillPolygon, consiga desenhar a seta para nos. Por isto o laço for, presente na linha 48. E o resultado é igual ao que foi gerado no tópico anterior.


Considerações finais

Aqui neste artigo, apresentei uma maneira bastante simples de lidar com cálculos matriciais. Tentei trazer algo, que de alguma forma pudesse ser interessante, e ao mesmo tempo, me permitisse explicar, da forma o mais simples possível como fazer tal cálculo. Porém, a modelagem das matrizes, vista neste artigo, não é a mais adequada, para nos permitir fazer todos os tipos de fatoração que são possíveis de serem feitas. Assim sendo, farei um novo artigo, que sairá em breve, onde explicarei uma forma mais adequada de modelar estas matrizes. E também trarei uma explicação voltada apenas para que você consiga tirar proveito desta forma de fatorar dados. Então até breve, nos vemos em um próximo artigo.


Arquivos anexados |
App1.mq5 (1.87 KB)
App2.mq5 (1.97 KB)
App3.mq5 (2.62 KB)
Redes neurais de maneira fácil (Parte 59): dicotomia do controle (DoC) Redes neurais de maneira fácil (Parte 59): dicotomia do controle (DoC)
No artigo anterior, nos familiarizamos com o transformador de decisões. Porém, o complexo ambiente estocástico do mercado de moedas não permitiu revelar totalmente o potencial do método apresentado. Hoje, quero apresentar a vocês um algoritmo focado em melhorar o desempenho dos algoritmos em ambientes estocásticos.
Algoritmos de otimização populacional: busca por difusão estocástica (Stochastic Diffusion Search, SDS) Algoritmos de otimização populacional: busca por difusão estocástica (Stochastic Diffusion Search, SDS)
O artigo aborda a busca por difusão estocástica, SDS, um algoritmo de otimização muito poderoso e prático, baseado nos princípios de passeio aleatório. O algoritmo permite encontrar soluções ótimas em espaços multidimensionais complexos, possuindo uma alta velocidade de convergência e a capacidade de evitar extremos locais.
Modelos de classificação da biblioteca Scikit-learn e sua exportação para o formato ONNX Modelos de classificação da biblioteca Scikit-learn e sua exportação para o formato ONNX
Neste artigo, exploraremos o uso de todos os modelos de classificação do pacote Scikit-learn para resolver o problema de classificação dos íris de Fisher, tentaremos convertê-los para o formato ONNX e usaremos os modelos resultantes em programas MQL5. Também compararemos a precisão dos modelos originais e suas versões ONNX no Iris dataset completo.
Anotação de dados na análise de série temporal (Parte 3): Exemplo de uso de anotação de dados Anotação de dados na análise de série temporal (Parte 3): Exemplo de uso de anotação de dados
Esta série de artigos apresenta várias técnicas destinadas a rotular séries temporais, técnicas essas que podem criar dados adequados à maioria dos modelos de inteligência artificial (IA). A rotulação de dados (ou anotação de dados) direcionada pode tornar o modelo de IA treinado mais alinhado aos objetivos e tarefas do usuário, melhorar a precisão do modelo e até mesmo ajudar o modelo a dar um salto qualitativo!