preview
Rede neural na prática: Pseudo Inversa (I)

Rede neural na prática: Pseudo Inversa (I)

MetaTrader 5Aprendizado de máquina | 20 junho 2024, 12:21
69 0
Daniel Jose
Daniel Jose

Introdução

Olá pessoal, e sejam bem-vindos a mais um artigo sobre Rede Neural.

No artigo anterior Rede neural na prática: Função de reta, falamos como poderíamos usando equações algébricas, conseguir determinar algumas das informações que estávamos procurando. Isto para que conseguíssemos criar uma equação. Que no nosso caso específico, é uma equação de reta. Já que o nosso pequeno banco de dados, de fato pode ser expresso como uma reta. Todo este material, envolvendo explicar sobre como redes neurais funcionam, é de fato algo que não é muito simples de ser feito, sem que eu saiba o nível de conhecimento de cada um a respeito de questões envolvendo matemática.

Por mais que muitos imaginem, pudessem imaginar que a coisa toda seria bem mais simples e direta. Já que existem bibliotecas e coisas do tipo, sendo disseminadas na WEB, dizendo que você pode desenvolver a sua própria rede neural. E prometendo muitas das vezes que você conseguirá criar uma inteligência artificial, que faça isto ou aquilo. No bem da verdade, as coisas não são tão simples assim.

Não quero, nesta pequena série, criar falsas expectativas em você meu caro leitor. Lhe dizendo que com pouco conhecimento, ou com quase nenhuma experiência, você vai conseguir construir algo realmente prático. E que poderá usar isto para tentar ganhar dinheiro, usando redes neurais ou inteligência artificial para operar no mercado. Pois quem lhe disser isto, estará mentido para você.

Criar, mesmo que uma rede neural simples, é algo realmente desafiador. E isto em muitos casos. Aqui quero mostrar, como você pode criar algo, que lhe sirva de inspiração para procurar estudar melhor sobre o tema. Este assunto sobre redes neurais, já vem sendo estudado a décadas, isto para ser modesto. Pois como falei antes, durante os três artigos sobre inteligência artificial, que antecederam estes sobre redes neurais. A coisa em si, é muito mais complicada, do que muitos ficam querendo dizer ou mostrar.

O fato de usar esta ou aquela função, não significará que sua rede neural será melhor ou pior. Apenas significará que você estará fazendo os cálculos usando esta ou aquela função. E isto se aplica, ao que será visto neste artigo.

O que veremos neste artigo, é algo, que perto dos três primeiros artigos, nesta série sobre redes neurais. Poderá fazer você pensar em desistir de estudar sobre o assunto. Mas também poderá lhe incentivar a estudar ainda mais sobre o tema. Aqui, veremos como podermos implementar, usando MQL5 puro, o cálculo de pseudo inversa. Apesar de não parecer assim tal assustador. O código que será visto, será de fato bem mais complicado, para os iniciantes, do que eu de fato gostaria de apresentar. Então não se assunte com o que será visto. Procure estudar o código com calma. Sem pressa e correria. Tentei deixar o código o mais simples, quanto foi possível fazer. Então ele não visa ser eficiente e de rápida execução. Muito pelo contrário, ele tem como objetivo, ser o mais didático possível. Mas mesmo assim, pelo fato de estarmos usando fatoração matricial. O código em si, é um pouco mais complicado do que muitos estejam de fato acostumados a ver, ou programar.

E sim, antes que alguém venha querer falar. Eu sei que o MQL5, tem uma função chamada PInv. Que implementa justamente o que iremos ver aqui. E sim, também sei que o MQL5, tem operações para trabalhar com matrizes. Só que aqui não iremos fazer os cálculos usando matrizes, como é definido no MQL5. Vamos usar arrays, que apesar de serem parecidos, tem uma lógica um pouco diferente, no que rege o acesso aos elementos presentes na memória.


Pseudo Inversa

Implementar este cálculo, não é uma tarefa das mais complicadas. Isto se você o entender. Basicamente temos que efetuar algumas multiplicações entre outras pequenas coisas. Isto usando apenas e somente uma única matriz. Ao fazer isto será gerado uma matriz que será o resultado de todas as fatorações internas, da pseudo Inversa.

Neste ponto, precisamos esclarecer algo. A tal pseudo inversa, pode ser fatorada, basicamente de duas maneira. Uma onde não ignoramos nenhum valor dentro da matriz, e outra onde usamos um limite mínimo para os valores presentes na matriz. Quando este limite mínimo não for alcançado, o valor daquele elemento na matriz será igual a zero. Não sou eu que estou impondo tal condição. São as regras presentes no modelo de cálculo, usados por todos os programas que calculam a pseudo inversa. Cada um dos casos tem um objetivo bem específico. Então o que vamos ver aqui, não é. E vou repetir: NÃO É, um cálculo definitivo para a pseudo inversa. O que vamos fazer, será um cálculo, cujo objetivo é gerar resultados que são obtidos por programas como MatLab, SciLab, entre outros que também implementam a pseudo inversa.

Como o MQL5, também consegue calcular a pseudo inversa. Você, meu caro e estimado leitor, poderá comparar os resultados obtidos pela aplicação que iremos implementar, com os mesmos resultados obtidos pelo cálculo da pseudo inversa da biblioteca do MQL5. Isto a fim de conseguir checar se está tudo correto. Então o objetivo deste artigo, não é implementar o cálculo da pseudo inversa. E sim entender o que está por trás do cálculo. Isto por que, saber e entender como os cálculos funcionam, permitirá a você, meu caro e estimado entusiasta. Entender por que e quando usar um ou outro método de cálculo em sua rede neural.

Então vamos começar fazendo o seguinte: Vamos implementar um código o mais simples possível para usar a pseudo inversa da biblioteca do MQL5. Este é o primeiro passo. Para que possamos saber que o cálculo que criaremos depois, de fato, funciona. Peço que por favor, não mude o código, até que você o tenha testado. Se você mudar qualquer coisa, antes de testar o mesmo. Ele poderá vir a dar resultados diferentes dos mostrados aqui. Então, meu amigo e entusiasta leitor. Primeiro teste o código que estou mostrando. Depois, e somente depois, se desejar o modifique para tentar entender melhor o que está acontecendo. Então, por favor, não faça nenhuma mudança no código, antes de testar o código original.

Muito bem, os códigos originais estão disponíveis no anexo deste artigo. Então vamos ver o primeiro deles. Ele é visto integralmente, logo abaixo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart()
05. {
06.     matrix M_A {{1, -100}, {1, -80}, {1, 30}, {1, 100}};
07. 
08.     Print("Moore–Penrose inverse of :");
09.     Print(M_A);
10.     Print("is :");
11.     Print(M_A.PInv());
12. }
13. //+------------------------------------------------------------------+

E o resultado da sua execução é visto na imagem abaixo:


Este código super mega simples, que você pode ver logo acima, é capaz de calcular a matriz pseudo inversa. E o resultado nos é mostrado no console, como você pode ver na imagem logo acima. Simples assim. Mas preste atenção a forma como o código é construído. Note que é algo muito, mas muito específico de ser criado. Você tem ao usar matrix e vector, toda uma gama de operações possíveis e perfeitamente funcionais. Porém elas está incluídas na biblioteca padrão do MQL5. Assim como também podem estar incluídas nas bibliotecas padrão de outras linguagens. Dependendo é claro, de que tipo de linguagem de programação, se esteja falando.

No entanto, apesar de isto ser muito bom, existem casos, em que precisamos, ou queremos que o código execute de uma determinada maneira. Seja por que, estamos querendo otimizar ele de alguma forma. Seja pelo simples fato, de que não queremos ficar gerando códigos que poderiam se executados de uma outra forma. Enfim, o motivo pouco importa. Se bem, que muitos gostam de dizer, que programadores C / C++ adoram ficar tentando reinventar a roda. Mas este não é o caso aqui. Neste artigo, quero mostrar a você, meu caro leitor, o que está por detrás deste complicado cálculo que você olha. Enxerga o resultado, mas não faz a mínima ideia de como este resultado foi obtido. E fazer qualquer tipo de estudo, sem de fato entender por que este, e não aquele resultado foi obtido. Não é de fato um estudo. É apenas crença. Ou seja, você vê e apenas acredita. Mas não sabe se é verdade ou mentira. Apenas acredita. E verdadeiros programadores, simplesmente não acreditam. Eles precisam tocar, ver, testar e experimentar para realmente acreditar no que está sendo gerado. Então vamos ver com este resultado mostrado na imagem acima, foi conseguido. Para isto, vamos a um novo tópico.


Entendendo o cálculo por trás da pseudo inversa

Se você já está satisfeito, em apenas ver os resultados, independente da forma como eles são alcançados. Ok. Este tópico daqui, já não irá lhe ser de nenhuma utilidade. Sendo completamente dispensável que você, perca seu tempo o lendo. Mas se você quer entender como o cálculo funciona. Bem, prepare. Pois, apesar de eu tentar fazer com que as coisas fiquem o mais simples possível. Ainda assim, será preciso que você preste atenção. Apesar de que, vou evitar ao máximo ficar mostrando fórmulas e coisas do tipo. Mas mesmo assim preste atenção. Pois o código a ser criado, pode parecer muito mais confuso, do que realmente ele é. Então vamos começar fazendo o seguinte: A pseuda inversa é calculada, basicamente usando multiplicação e uma matriz inversa.

Esta multiplicação é relativamente simples de ser feita. Muitos utilizam alguns recursos, que considero desnecessários para conseguir efetuar a multiplicação. Nos artigos onde falei sobre matrizes. Mostrei um método para efetuar a multiplicação. Mas aquele método, é para um cenário bastante específico. Aqui precisamos de um método, um pouco mais genérico. Isto por que será preciso fazer algumas coisas, um tanto quanto diferentes do que foi visto no artigo sobre matrizes.

Para facilitar, vamos ver o código em pequenos fragmentos. Cada um explicando algo. Começando com a multiplicação, que pode ser vista no fragmento logo abaixo:

01. //+------------------------------------------------------------------+
02. void Generic_Matrix_A_x_B(const double &A[], const uint A_Row, const double &B[], const uint B_Line, double &R[], uint &R_Row)
03. {
04.     uint A_Line = (uint)(A.Size() / A_Row),
05.          B_Row  = (uint)(B.Size() / B_Line);
06. 
07.     if (A_Row != B_Line)
08.     {
09.         Print("Operation cannot be performed because the number of rows is different from that of columns...");
10.         B_Row = (uint)(1 / MathAbs(0));
11.     }
12.     if (!ArrayIsDynamic(R))
13.     {
14.         Print("Response array must be of the dynamic type...");
15.         B_Row = (uint)(1 / MathAbs(0));
16.     }
17.     ArrayResize(R, A_Line * (R_Row = B_Row));
18.     ZeroMemory(R);
19.     for (uint cp = 0, Ai = 0, Bi = 0; cp < R.Size(); cp++, Bi = ((++Bi) == B_Row ? 0 : Bi), Ai += (Bi == 0 ? A_Row : 0))
20.         for (uint c = 0; c < A_Row; c++)
21.             R[cp] += (A[Ai + c] * B[Bi + (c * B_Row)]);
22. }
23. //+------------------------------------------------------------------+

Ao olhar este fragmento de código acima, você já deve estar se sentindo sem chão. Ou implorando pelo colo da sua mãe. Talvez esteja correndo apavorado, fugindo para as colinas, pois o fim dos tempos esta chegando. Alguns vão ajoelhar e pedir perdão para DEUS, por todos os seus pecados. Mas, brincadeiras a parte. Este código é muito simples. Mesmo que pareça algo de outro mundo ou extremamente complicado.

Talvez o motivo de você o estar achando complicado. É pelo fato de ele estar muito adensado. Com várias coisas sendo feitas aparentemente ao mesmo tempo. Peço desculpas a quem está chegando agora. Mas quem vem acompanhando meus artigos, sabe como é minha forma de escrever os códigos. E talvez esteja até vendo que este formato é bastante comum vindos de mim. Bem, mas vamos entender o que está acontecendo aqui. Pois diferente do código mostrando antes. Este é genérico, tento inclusive um teste para checar se podemos ou não multiplicar duas matrizes. O que é bastante legal, apesar de não ser muito amigável.

Muito bem, na linha dois temos a declaração do procedimento. Você deve tomar cuidado em declarar e passar os parâmetros nos locais corretos. Então a matriz A será multiplicada pela matriz B e o resultado será colocado na matriz R. Você precisa dizer quantas colunas tem na matriz A e quantas linhas tem na matriz B. O último argumento retornará a quantidade de colunas na matriz R. O motivo de retornarmos o número de colunas na matriz R, será visto em breve. Então por hora, não se preocupe com isto.

Beleza. Nas linhas quatro e cinco, calculamos os valores restantes, isto para que você, não os precise informar. Agora na linha sete, fazemos um pequeno teste, que tem como objetivo, verificar se as matrizes podem ser multiplicadas. Por isto, a ordem em que as matrizes são passadas importa. Não é como um código voltado a cálculos escalares. Cálculos matriciais precisamos tomar alguns cuidados.

Ok, se a multiplicação não puder ser feita. Na linha nove iremos imprimir uma mensagem no console do MetaTrader 5. E logo depois na linha dez, teremos o disparo de um erro de RUN-TIME. Este fará com que a aplicação que esteja tentando fazer a multiplicação das matrizes, venha a ser encerrada. Se você notar este erro sendo impresso no console. Veja se a mensagem da linha nove, também está aparecendo. Caso isto esteja acontecendo, o erro não está neste código mostrado no fragmento. O erro está no ponto, onde este código está sendo chamado. Sei que não é nada amistoso, e tão pouco gentil, forçar um erro de RUN-TIME, para encerrar uma aplicação. Mas fazendo isto, com toda a certeza, evitamos que a aplicação nos mostre resultados equivocados.

Agora vem a parte que começa a assustar, o coração de muitos. Na linha 17, alocamos memória para comportar todo o resultado que será gerado. Desta forma a matriz R, deverá ser do tipo dinâmica no chamador. Não use um array estático, pois se você fizer isto, a linha 12 irá perceber isto. E o código será encerrado com um erro de RUN-TIME, com uma mensagem na linha 14 indicando o motivo do encerramento.

Um detalhe, nesta forma de gerar o erro, é que quando em execução a aplicação estará tentando fazer uma divisão por zero. O que fará com que o processador ( CPU MESMO ), venha a disparar uma interrupção interna. Esta fará o sistema operacional tomar uma medida, contra a aplicação que gerou a tal interrupção, fazendo com que ela seja encerrada. Mesmo que o sistema operacional não venha a fazer nada, a CPU estará em modo de interrupção. Fazendo com que, mesmo um sistema embarcado e dedicado a executar a aplicação, seja paralisado.

Agora quero que você, meu caro leitor, preste atenção ao seguinte: Aqui estou usando um macete, para evitar que o compilador perceba que o erro de RUN-TIME venha a ser gerado. Se não fizesse da forma como você pode observar no código. O compilador impediria que o código fosse compilado. Mesmo que a linha que irá gerar o erro de RUN-TIME dificilmente fosse executada, em situações normais. Sempre que você desejar, forçar o final da execução de um programa, você pode usar um truque parecido. Funciona sempre. Apesar de não ser muito educado fazer as coisas assim, pois o usuário pode ficar irritado com a sua aplicação, ou com você que a programou. Então, use com moderação.

Na linha 18 limpamos completamente qualquer coisa que esteja na memória alocada. Normalmente alguns compiladores fazem esta limpeza para nos. Mas depois de já está programando em MQL5, a algum tempo, acabei notando que ele naturalmente não faz a limpeza da memória alocada dinamicamente. Acredito que o motivo para isto, seja o fato de que, memória alocada dinamicamente, no MetaTrader 5, visa ser usada em buffers de indicadores. E como tais buffers são escritos conforme os dados vão chegando e sendo calculados, não faz sentido limpar a memória. Mesmo por que esta limpeza consome um tempo para ser efetuada. Tempo este que pode ser melhor utilizado para outras tarefas. Sendo assim, cabe a nos, fazermos esta limpeza e garantirmos que não estamos usando valores do tipo lixo, em nenhum cálculo. Preste atenção a isto em seus programas, caso você esteja fazendo uso de memória alocada dinamicamente, e que não seja usada como buffers de indicadores personalizados.

Ok. Agora vem a parte divertida do procedimento. Multiplicar a matriz A pela matriz B e colocar o resultado na matriz R. Isto é feito em duas linhas. A linha 19, talvez pareça muito complicada a princípio. Mas vamos ver o que está acontecendo nela, com calma. A ideia aqui é torna totalmente dinâmica e genérica, a multiplicação de duas matrizes. Ou seja, pouco importa se uma tem mais ou menos linhas ou colunas. Se o procedimento chegou até neste ponto, as matrizes serão multiplicadas.

Pois bem, da mesma forma que, a ordem das matrizes afeta o resultado. Aqui na linha 19, a ordem em que as coisas estão acontecendo também afeta os resultados. Mas para não tomar muito tempo, vou simplificar a explicação. Para entender o que está acontecendo, leia o código da forma como ele está escrito. Ou seja, da esquerda para a direita. Termo a termo. Pois é assim que o compilador o lê para construir o executável. Então apesar de parecer confuso, ele não é confuso. Apenas está bem adensado, diferente do que grande parte costuma utilizar. Mas de qualquer forma a ideia aqui é ler coluna por coluna da matriz A, e multiplicar o valor, lendo linha por linha na matriz B. Por isto a ordem dos fatores influenciará o resultado. Então você não precisa se preocupar se a matriz B está configurada, ou melhor dizendo organizada em uma matriz de linha ou coluna. Ela pode estar organizada da mesma maneira que a matriz A. E mesmo assim este procedimento de multiplicação conseguirá ser executado com sucesso.

Bem, a primeira das operações a serem construídas, para obter o valor da pseudo inversa, está pronta. Então vamos olhar o tipo de cálculo que precisamos fazer. Isto para sabermos o que precisaremos implementar. Pois talvez, o que precisará ser implementado, pode ser voltado para apenas ajudar a fatorar o valor para a pseudo inversa. Não precisando ser de fato uma solução genérica, como foi o caso da multiplicação.

Ok, a fórmula para calcular a pseudo inversa é vista logo abaixo.

Bem, nesta fórmula, M representa a matriz usada. Veja que ela é sempre a mesma, o tempo todo. Porém olhando esta fórmula, notamos que precisamos fazer uma multiplicação entre a matriz original e sua transposta. Logo depois pegar o resultado e encontrar o valor da matriz inversa. E no final multiplicar a matriz transposta pelo resultado da inversa. Coisa simples de fazer, não é mesmo ?!?! Bem, mas aqui podemos pegar, ou melhor, criar alguns atalhos. Claro que para criar o atalho perfeito, precisaríamos modelar um procedimento, que fosse capaz apenas de calcular a pseudo inversa. Mas criar tal procedimento, apesar de não ser uma tarefa muito complicada, tornaria muito complicado explicar como o mesmo funciona. Para que você, meu caro leitor tenha uma ideia do que estou falando, vamos fazer o seguinte. Irei unir a multiplicação da transposta e da matriz original em um único procedimento. Normalmente, quando estudamos isto na faculdade, nos pedem para fazer isto em dois blocos. Ou seja, primeiro criamos uma matriz transposta, e depois usamos o procedimento de multiplicação para conseguir o resultado final. Porém, podemos fazer um procedimento, que dispense estes dois passos, e faça isto em um único passo. E mesmo sendo algo muito fácil de implementar, você verá que o código é bem complicado de entender.

Assim sendo, veja no fragmento abaixo, como efetuar a operação que está entre parênteses na imagem acima.

01. //+------------------------------------------------------------------+
02. void Matrix_A_x_Transposed(const double &A[], const uint A_Row, double &R[], uint &R_Row)
03. {
04.     uint BL = (uint)(A.Size() / A_Row);
05.     if (!ArrayIsDynamic(R))
06.     {
07.         Print("Response array must be of the dynamic type...");
08.         BL = (uint)(1 / MathAbs(0));
09.     }
10.     ArrayResize(R, (uint) MathPow(R_Row = (uint)(A.Size() / A_Row), 2));
11.     ZeroMemory(R);
12.     for (uint cp = 0, Ai = 0, Bi = 0; cp < R.Size(); cp++, Bi = ((++Bi) == BL ? 0 : Bi), Ai += (Bi == 0 ? A_Row : 0))
13.         for (uint c = 0; c < A_Row; c++)
14.             R[cp] += (A[c + Ai] * A[c + (Bi * A_Row)]);
15. }
16. //+------------------------------------------------------------------+

Veja que o este fragmento se parece muito com o fragmento anterior. Salvo o fato de que a linha onde o resultado é obtido, é ligeiramente diferente entre ambos fragmentos. Mas mesmo assim, este fragmento é capaz de fazer o cálculo, a fim de conseguir obter o resultado, onde multiplicamos a matriz original, por sua transposta. Nos poupando assim, o trabalho de termos de criar uma matriz transposta. Este tipo de coisa é a tal da otimização que podemos fazer. Claro que, também podemos acumular neste mesmo procedimento, ainda mais coisas, como gerar a inversa da matriz resultante. E até mesmo já fazer a multiplicação da inversa com a transposta da matriz original. Mas como você, meu caro leitor pode imaginar, cada um destes passos que formos adicionando, irá trazer ainda mais complexidade para o procedimento. Não de forma geral. Mas de forma localizada. Assim prefiro apresentar ele aos pouco. Para que você possa compreender o que está acontecendo. E até mesmo testar as coisas, se assim desejar.

Mas como não quero, e não vou, de fato, recriar a roda. E este nem é o objetivo deste artigo. Não vamos acumular tudo em uma única função. Apenas mostrei este fragmento acima, para que você possa entender que nem tudo que estudamos, na escola, se aplicará na prática. Muitas coisas são de fato, adotadas de forma a serem otimizadas para um determinado problema. E o fato de otimizarmos algo, torna a execução bem mais rápida do que um processo mais generalista.

Assim vamos fazer o seguinte: Já temos o cálculo da multiplicação. Precisamos agora de um cálculo que gere a inversa da matriz obtida. Existem diversas formas diferentes de programar esta inversa. Apesar de existirem poucas formas matemáticas, de expressar como fazer, a forma de programar a coisa em sim, pode ser muito distinta. Fazendo com que um algorítimo seja mais rápido do que outro. Mas de qualquer forma o que nos interessa, é o resultado correto. A forma de gerar ele pouco importa.

Mas para gerar a matriz inversa, gosto de usar uma forma bem específica, que leva em conta o uso da determinante da matriz. Neste momento, pode ser que você goste de usar um ou outro método para encontrar a determinante. Mas eu, por costume gosto de usar o método de SARRUS. Por achar mais simples de programar ele. O método de SARRUS, para quem não sabe, faz o cálculo, da determinante, baseando-se no valor da diagonais. Programar isto, é algo bem interessante. No fragmento abaixo, vemos uma proposta de como fazer isto. Funciona para qualquer matriz. Ou melhor dizendo, array. Desde que ele seja quadrado.

01. //+------------------------------------------------------------------+
02. double Determinant(const double &A[])
03. {
04. #define def_Diagonal(a, b)  {                                                                                                                       \
05.                 Tmp = 1;                                                                                                                            \
06.                 for (uint cp = a, cc = (a == 0 ? 0 : cp - 1), ct = 0; (a ? cp > 0 : cp < A_Row); cc = (a ? (--cp) - 1 : ++cp), ct = 0, Tmp = 1)     \
07.                 {                                                                                                                                   \
08.                     do {                                                                                                                            \
09.                         for (; (ct < A_Row); cc += b, ct++)                                                                                         \
10.                             if ((cc / A_Row) != ct) break; else Tmp *= A[cc];                                                                       \
11.                         cc = (a ? cc + A_Row : cc - A_Row);                                                                                         \
12.                     }while (ct < A_Row);                                                                                                            \
13.                     Result +=  (Tmp * (a ? -1 : 1));                                                                                                \
14.                 }                                                                                                                                   \
15.                             }
16. 
17.     uint A_Row, A_Size = A.Size();
18.     double Result, Tmp;
19. 
20.     if (A_Size == 1)
21.         return A[0];
22.     Tmp = MathSqrt(A_Size);
23.     A_Row = (uint)MathFloor(Tmp);
24.     if ((A_Row != (uint)MathCeil(Tmp)) || (!A_Size))
25.     {
26.         Print("The matrix needs to be square");
27.         A_Row = (uint)(1 / MathAbs(0));
28.     }
29.     if (A_Row == 2)
30.         return (A[0] * A[3]) - (A[1] * A[2]);
31.     Result = 0;
32. 
33.     def_Diagonal(0, A_Row + 1);
34.     def_Diagonal(A_Row, A_Row - 1);
35. 
36.     return Result;
37. 
38. #undef def_Diagonal
39. }
40. //+------------------------------------------------------------------+

Este lindo fragmento que você vê em sua frente, meu caro leitor, consegue estabelecer o valor da determinante de uma matriz. Aqui, fazemos algo tão lindo e maravilhoso, que praticamente não preciso descrever quase nada. Já que o código se alto explica.

Veja bem. Temos uma macro sendo definida na linha quatro. Esta consegue, varrer o array, ou no nosso caso uma matriz, que está dentro do array. Na diagonal. Isto mesmo, A matemática presente neste código, permite que você calcule o valor das diagonais. Uma a uma. Tanto no sentido da diagonal principal, quanto no sentido da diagonal secundária. E tudo isto de forma linda, poética e bonita. Note que tudo que precisamos informar ao código é o array que contem a nossa matriz. O valor retornado é o determinante da matriz. Porém o método de SARRUS, dentro deste código, tem uma limitação. Quando a matriz tem uma dimensão, o determinante é a própria matriz. Então retornamos isto logo de início. Você pode ver isto na linha 21. Se a matriz estiver vazia, ou não for quadrada, na linha 27 iremos disparar um erro de RUN-TIME, evitando que o código continue a executar. Se a matriz é do tipo dois por dois, o cálculo das diagonais, não passará pela macro. Ele será feito antes. Isto na linha 30. Já em qualquer outro caso, o cálculo da determinante passará pela macro. Primeiro na linha 33 onde faremos o cálculo da diagonal principal. E na linha 34 o cálculo da diagonal secundária. Se você não conseguiu entender o que está ocorrendo, veja a figura abaixo, onde mostro um exemplo do que está acontecendo. Isto para quem não conhece o método de SARRUS.


Nesta figura, a região em vermelho representa a matriz, na qual queremos saber o determinante. Já a região em azul é a cópia virtual de parte dos elementos da matriz. O código da macro faz justamente este cálculo visto na figura. Retornando para nos o determinante que é visto na figura. Ou seja, o valor 79.


Considerações finais

Bem, meu caro leitor, chegamos ao fim de mais um artigo. Porém neste ainda não fizemos todos os procedimentos necessários para calcular o valor da pseudo inversa. Isto será finalizado no próximo artigo. Onde veremos a aplicação que de fato fará tal tarefa. Mesmo que você diga que podemos usar o código do anexo deste artigo. O problema é que ele não nos dá toda a liberdade para usar qualquer tipo de estrutura. Sendo que para usar o PInv, precisamos de fato, estar usando um tipo matrix. E no que estou mostrando, como meios de explicar o cálculo, podemos usar qualquer modelagem de dados. Basta fazer os devidos ajustes, para conseguir usar qualquer coisa.  Então nos vemos no próximo artigo. 


Arquivos anexados |
Anexo_01.mq5 (0.42 KB)
Anotação de dados na análise de série temporal (Parte 5): Aplicação e teste de um EA usando Socket Anotação de dados na análise de série temporal (Parte 5): Aplicação e teste de um EA usando Socket
Nesta série de artigos, apresentamos vários métodos de anotação de séries temporais que podem criar dados compatíveis com a maioria dos modelos de inteligência artificial (IA). A anotação precisa dos dados pode tornar o modelo de IA treinado mais alinhado com os objetivos e tarefas dos usuários, aumentar a precisão do modelo e até ajudar a alcançar uma melhoria significativa na qualidade!
Implementação do teste aumentado de Dickey-Fuller no MQL5 Implementação do teste aumentado de Dickey-Fuller no MQL5
Neste artigo, vamos mostrar como implementar o teste aumentado de Dickey-Fuller e sua aplicação para realizar testes de cointegração usando o método de Engle-Granger.
Desenvolvimento de um Cliente MQTT para o MetaTrader 5: Metodologia TDD (Parte 5) Desenvolvimento de um Cliente MQTT para o MetaTrader 5: Metodologia TDD (Parte 5)
Este artigo é a quinta parte de uma série que descreve as etapas de desenvolvimento de um cliente MQL5 nativo para o protocolo MQTT 5.0. Nesta parte, vamos detalhar a estrutura dos pacotes PUBLISH, configuraremos seus flags de publicação, codificaremos os nomes dos tópicos e estabeleceremos identificadores de pacotes quando necessário.
Introdução ao MQL5 (Parte 2): Variáveis pré-definidas, funções gerais e operadores de fluxo de controle Introdução ao MQL5 (Parte 2): Variáveis pré-definidas, funções gerais e operadores de fluxo de controle
Neste artigo, continuamos a explorar a linguagem de programação MQL5. Esta série de artigos não é apenas um material didático, mas sim uma porta de entrada para o mundo da programação. O que os torna especiais? Eu me esforcei para manter a simplicidade nas explicações, tornando conceitos complexos acessíveis a todos. Para obter os melhores resultados, é necessário praticar ativamente tudo o que discutimos. Só assim você obterá o máximo proveito desses artigos.