English Русский Español 日本語 Português
preview
Matrix-Faktorisierung: Die Grundlagen

Matrix-Faktorisierung: Die Grundlagen

MetaTrader 5Beispiele | 10 September 2024, 09:53
39 0
Daniel Jose
Daniel Jose

Einführung

Herzlich willkommen zu meinem neuen Artikel mit lehrreichem Inhalt.

In diesem Artikel werden wir über Matrixberechnungen sprechen. Liebe Leserinnen und Leser, weigern Sie sich nicht vorschnell, diesen Artikel zu lesen, weil Sie denken, dass wir über etwas rein Mathematisches und zu Kompliziertes sprechen werden. Im Gegensatz zu dem, was viele Leute denken, ist ein guter Programmierer nicht jemand, der ein riesiges Programm schreibt, das nur er verstehen kann, oder jemand, der Code in einer angesagten Programmiersprache schreibt. Ein echter und guter Programmierer versteht, dass ein Computer nichts anderes ist als eine Rechenmaschine, der wir sagen können, wie Berechnungen durchgeführt werden sollen. Es spielt keine Rolle, was genau wir erstellen, es kann ein einfacher Texteditor sein, der keinen mathematischen Code enthält. Aber das ist nur eine Illusion. Sogar ein Texteditor enthält eine ganze Menge Mathematik in seinem Code, besonders wenn er eine Rechtschreibprüfung eingebaut hat.

Grundsätzlich gibt es in der Programmierung zwei Möglichkeiten, eine Faktorisierung oder Berechnung durchzuführen, wenn Sie es so nennen wollen. Die erste Möglichkeit ist die Berechnung in skalarer Form, d.h. unter Verwendung der von der Sprache bereitgestellten Funktionen, um die Formel wie unten dargestellt zu schreiben:


Mit dieser Formel, die jeder kennt, können Sie die Wurzeln einer quadratischen Funktion berechnen. Es kann leicht in jeder Programmiersprache geschrieben werden, indem die in dieser Sprache verfügbaren Funktionen und Ressourcen verwendet werden. Aber es gibt noch eine andere Möglichkeit, Berechnungen durchzuführen: die Matrix oder den Vektor, wie manche Leute es lieber nennen. In diesem Artikel werden wir es „Matrix“ nennen, weil wir Matrizen verwenden werden, um Dinge darzustellen. Diese Art des Factorings sieht folgendermaßen aus:


Einige Enthusiasten, die gerade erst mit dem Programmieren anfangen, haben keine Ahnung, wie man das in Code schreibt. Am Ende müssen sie die Matrixnotation in eine Skalarnotation umwandeln, um das gleiche Ergebnis zu erhalten.

Die Frage ist nicht, ob es richtig oder falsch ist. Wenn das erhaltene Ergebnis korrekt ist, spielt es keine große Rolle. In diesem Fall zeigt diese Matrix, dass sich etwas um einen bestimmten Punkt dreht. In diesem Artikel möchte ich Ihnen, liebe Leser, zeigen, wie Sie mit MQL5 oder einer anderen Sprache diese Matrixform direkt in den Code schreiben können, ohne sie in eine skalare Form umwandeln zu müssen. Viele Menschen denken, dass dies schwer zu bewerkstelligen ist. In Wirklichkeit ist alles viel einfacher, als es scheint. Folgen Sie also dem Artikel, um zu erfahren, wie Sie dies tun können.


Warum Matrizen und nicht skalare Formen verwenden?

Bevor wir uns ansehen, wie man Code schreibt, sollten wir herausfinden, wie man die eine oder andere Methode zur Implementierung der Faktorisierung wählt.

Wenn man nach Informationen über eine Programmiersprache sucht, stößt man natürlich auf die skalare Form der Codeerstellung, aber warum? Der Grund dafür ist, dass die Verwendung der Matrixform beim Schreiben des Codes etwas verwirrender ist. Um dies zu verstehen, versuchen Sie, die oben gezeigte Matrix oder eine andere Matrix in Form von Codezeilen zu schreiben. Sie werden sehen, dass das etwas unangenehm ist. Der Code sieht seltsam aus und macht nicht viel Sinn. In skalarer Form wird der Code viel leichter zu verstehen sein. Aus diesem Grund werden Sie nie jemanden sehen, der Faktorisierungscode in Matrixform schreibt.

Einige Faktorisierungen sind jedoch in Matrixform viel einfacher zu schreiben als in ihrer skalaren Entsprechung. Zum Beispiel, wenn Sie große Datenmengen verarbeiten müssen, die sich leicht als mehrdimensionales Array darstellen lassen. Um dies zu verstehen, sollten wir uns zunächst ein Vektorbild vorstellen. Anstatt einen Bildschirm Pixel für Pixel zu zeichnen, verwenden wir Vektoren. So können wir auf extrem einfache Weise mit Hilfe von Matrizen drehen, skalieren, Scherdeformationen und andere Manipulationen durchführen. Oder anders gesagt, das Schreiben von Berechnungscode zur Verwendung von Matrizen. Die gleichen Transformationen können auch in skalarer Form durchgeführt werden, aber die Verwendung von Matrizen ist viel einfacher.

Wenn Vektorgrafiken nicht komplex genug erscheinen, können wir über 3D-Objekte nachdenken. Vielleicht verstehen Sie jetzt, warum Matrixfaktorisierungen so interessant sind.

Bei einem 3D-Objekt lässt sich jede Transformation viel einfacher mit Hilfe von Matrizen durchführen. Das Gleiche auf skalare Weise zu tun, ist sehr schwierig und zeitaufwändig. Das ist möglich, aber so schwierig, dass es entmutigend ist. Stellen Sie sich vor, Sie müssten eine orthografische Projektion eines 3D-Objekts mit Hilfe von Formeln durchführen, die den zu Beginn des Artikels genannten ähneln. So etwas zu implementieren wäre ein Alptraum. Bei der Erstellung von Code, der Matrizen verwendet, spielt die Komplexität des 3D-Objekts jedoch keine Rolle: Die Erstellung einer orthografischen Projektion ist sehr einfach.

Dies wird in Grafikkarten und 3D-Modellierungsprogrammen verwendet. In der Tat verwenden sie bei ihren Berechnungen Matrizen, auch wenn Sie das nicht bemerken. Wann sollte man also eine Form und wann eine andere verwenden, um einen Faktorisierungscode zu schreiben? Das hängt von Ihrer Erfahrung und Ihrem Wissen ab. Es gibt keine starren Regeln, die Sie zwingen, die eine oder andere Methode anzuwenden. Es ist jedoch wichtig zu wissen, wie man beide Methoden programmiert, um alles so schnell, einfach und effizient wie möglich zu machen.

Da die Skalarform weit verbreitet ist und wortwörtlich programmiert wird, werden wir uns hier nur darauf konzentrieren, wie man Code in Matrixform schreibt. Um es einfacher und verständlicher zu machen, werden wir es in einzelne Themen unterteilen. Fangen wir gleich damit an.


Skalare Methode

Hier werden Sie etwas sehr Einfaches und Klares sehen. Bitte denken Sie daran, dass dieser Artikel nur für Bildungszwecke gedacht ist. Es ist nicht dazu gedacht, etwas Bestimmtes zu programmieren.

Lassen Sie uns zunächst ein kleines Programm in MQL5 erstellen. Was wir hier behandeln, kann auf jede andere Sprache angewendet werden, die Sie verwenden möchten. Es hängt alles davon ab, wie gut Sie die Sprache beherrschen. Wir beginnen mit einem sehr einfachen Code (siehe unten).

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

Wie Sie sehen können, ist dieser Code ein Indikator. Wir könnten jeden anderen Indikator verwenden, aber der Indikator ist aus mehreren Gründen interessanter: Wir können einige Dinge mit ihm implementieren und er erlaubt uns, bestimmte Aufrufe von MetaTrader 5 abzufangen.

Dieser Code ist sehr einfach und macht fast nichts. Es ändert einfach die Farbe des Chartfensters in weiß oder eine andere Farbe, die Sie verwenden möchten. Bitte beachten Sie, dass wir in der sechsten Zeile die MQL5-Standardbibliothek verwenden, um den generierten Code so weit wie möglich zu vereinfachen. In dieser Zeile teile ich dem Compiler mit, dass er die Header-Datei Canvas.mqh hinzufügen soll, die bei der Installation von MetaTrader 5 standardmäßig erstellt wird. Dies vereinfacht den Stoff, den wir in diesem Artikel behandeln werden, erheblich. Dann deklarieren wir in Zeile 8 eine globale Variable, damit wir auf die Klasse CCanvas zugreifen können. Da wir von einer Klasse sprechen, müssen wir sie auf eine bestimmte Weise verwenden. Ein wichtiges Detail: Normalerweise verwende ich gerne Klassen als Zeiger. Aber da viele vielleicht Zweifel daran haben, warum ich dies oder jenes tue, werde ich, um die Dinge so weit wie möglich zu vereinfachen, eine Klasse so verwenden, wie sie jeder normalerweise verwendet, d.h. als eine einfache Variable, die es uns ermöglicht, auf etwas zu verweisen.

In den Zeilen 15 und 16 legen wir also die Größe des Chart-Fensters fest, in dem der Indikator platziert wird. Beachten Sie, dass wir dafür kein Fenster erstellen. Dies ist in Zeile 3 angegeben. Da der Indikator überhaupt nichts verfolgt, vermeiden wir Compiler-Warnmeldungen. Hierfür verwenden wir Zeile 4. Wenn wir diesen Indikator in einem Unterfenster anwenden, werden die in den Zeilen 15 und 16 angezeigten Informationen anders aussehen.

In Zeile 18 teilen wir der Canvas-Klasse mit, dass wir einen Bereich erstellen wollen, in dem wir etwas zeichnen wollen. Dieser Bereich reicht von der linken oberen Ecke (zweiter und dritter Parameter) bis zur linken unteren Ecke (die nächsten beiden Parameter). Der erste Parameter ist der Name des von der CCanvas-Klasse erstellten Objekts. Der letzte Parameter gibt an, welche Art von Kombination zum Zeichnen in diesem Bereich verwendet werden soll. Genauere Informationen zu diesen Zeichenmodi finden Sie in der Dokumentation. Wir werden ein Modell verwenden, das Alpha als Farbe hat, d. h. wir können für alles, was wir im ausgewählten Bereich zeichnen, Transparenz erzeugen.

Der nächste Schritt ist die Bereinigung des Bereichs. Dies geschieht in Zeile 19, und unmittelbar danach, in Zeile 21, wird angegeben, dass die Informationen im Speicher auf dem Bildschirm angezeigt werden sollen. Es ist wichtig zu verstehen, dass die Informationen, die wir mit CCanvas in einem Bereich platzieren, nicht zur gleichen Zeit auf dem Bildschirm erscheinen, zu der wir ihn erstellen. Alle Zeichnungen werden zunächst aus dem Gedächtnis erstellt. Sie werden erst auf dem Bildschirm angezeigt, wenn Zeile 21 ausgeführt wird. Es ist alles ganz einfach. Nachdem wir nun die Grundlagen kennen, wollen wir uns ansehen, was wir auf dem Bildschirm darstellen werden. Dies wird im folgenden Code gezeigt.

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

Beachten Sie, dass wir hier eine skalare Übersetzung der zu Beginn des Artikels angegebenen Formel vornehmen, die die Matrixnotation verwendet. Mit anderen Worten, wir werden das Objekt drehen. Aber was ist dieses Objekt? Um dies zu verstehen, werfen Sie einen Blick auf das folgende Bild. Dies werden wir sehen, wenn wir den Code in MetaTrader 5 ausführen.


Das Objekt, das wir drehen, ist der Pfeil. Aber wenn Sie sich diesen Code ansehen, können Sie erkennen, wo der Pfeil eingezeichnet ist? Sehen Sie sich Zeile 38 an, wo wir die Funktion ARROW aufrufen. Der PFEIL befindet sich in Zeile 12. Offensichtlich muss der Pfeil irgendwo innerhalb der Funktion in Zeile 12 sein, aber wo? Der Pfeil wird in den Zeilen 14 und 15 definiert. Beachten Sie, dass wir zwei Arrays haben, um diesen Pfeil darzustellen. Hier zeichnen wir es auf eine sehr einfache Weise. Jetzt beginnt der interessanteste Teil.

Dieser Pfeil ist in absoluten Zahlen definiert. Das heißt, wir stellen sie so dar, wie sie sich in der realen Welt darstellen würde, aber wir können sie auch virtuell darstellen. Anstelle von Ganzzahlwerten können Sie auch Fließkommazahlen verwenden. Durch Multiplikation der Werte, die den Pfeil darstellen, mit einem beliebigen Skalar können wir also seine Größe bestimmen. Um den Fall nicht zu verkomplizieren, haben wir dies hier jedoch nicht getan. Ich möchte es so einfach wie möglich machen, damit Sie, liebe Leserinnen und Leser, verstehen können, wie man die Matrixfaktorisierung umsetzt.

In Zeile 16 gibt es etwas, das viele Menschen verwirren könnte. Aber der Grund, warum er so geschrieben ist, ist leicht zu verstehen. Da wir nicht im Voraus wissen, wie viele Punkte wir zum Zeichnen des Objekts verwenden werden, und wir ein Zwischenfeld benötigen, um es in Zeile 24 zu zeichnen, müssen wir der Anwendung irgendwie mitteilen, dass sie Speicher für uns zuweisen soll. Mit dem Formular in Zeile 16 teilen wir dem Compiler mit, dass er so viel Platz zuweisen soll, wie er zum Zeichnen der Punkte benötigen würde. Das heißt, wir haben ein dynamisches Array, das verwendet wird, aber statisch zugeordnet ist. Wenn wir diese Art der Codeerstellung nicht verwenden würden, müssten wir denselben Code wie in der Funktion Arrow erstellen, der wie im folgenden Code zusammengesetzt ist.

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

Beachten Sie, dass in Zeile 16 nun eine dynamische Array-Anzeige vorhanden ist. Diese Art von Array definiert den Raum, der während der Ausführung verwendet wird. Wir müssen die folgenden Änderungen vornehmen. In den Zeilen 18 und 19 des Codes müssen wir den erforderlichen Speicherplatz zuweisen, damit das Programm während der Ausführung nicht abnormal abbricht. Außerdem müssen wir in diesem Code den in den Zeilen 30 und 31 zugewiesenen Speicher an das Betriebssystem zurückgeben, was als gute Programmierpraxis gilt. Viele Programmierer neigen dazu, zugewiesene Ressourcen nicht zurückzugeben, was jedoch dazu führt, dass die Anwendung Ressourcen verbraucht: Die Anwendung gibt sie nicht frei, was ihre Verwaltung erschwert. Aber abgesehen von diesen kleinen Unterschieden, die Sie zwischen dem Snippet und dem Hauptcode sehen können, funktioniert alles andere gleich.

Kehren wir nun zum Hauptcode zurück. Die Schleife in Zeile 18 durchläuft alle Punkte, die Teil der zu drehenden Form sind.

Achten Sie jetzt auf etwas Interessantes. Wir drehen die Form und verschieben sie an eine bestimmte Stelle. Genauer gesagt, verschieben wir ihn an den X- und Y-Punkt, der in diesem Fall die Mitte des Bildschirms ist. In Zeile 38 definieren wir den Ort und den Drehwinkel, der 60 Grad beträgt.

Da alle trigonometrischen Funktionen standardmäßig Werte in Bogenmaß und nicht in Grad verwenden, müssen wir Grad in Bogenmaß umrechnen. Genau das wird in der Definition von Zeile 10 getan. Auf diese Weise ist es natürlicher zu sagen, um wie viel sich die Form drehen soll, anstatt den Wert in Bogenmaß auszudrücken, was in vielen Fällen ziemlich verwirrend sein kann.

Daher wenden wir für jeden der Punkte in der Matrix die unten stehende Formel an.


Das Bild wird gegen den Uhrzeigersinn gedreht, sodass Null Grad der Neun-Uhr-Position auf der Uhr entspricht. Zur Verdeutlichung: 90 Grad sind sechs Stunden, 180 Grad sind drei Stunden und 270 Grad sind zwölf Stunden. Denken Sie daran, dass die Drehung gegen den Uhrzeigersinn erfolgt. Um dieses Verhalten zu ändern, müssen Sie die Berechnungsmethode ändern. Es gibt nichts Kompliziertes daran, im Gegenteil, alles ist sehr einfach. Um die Drehung im Uhrzeigersinn zu bewerten, müssen Sie die Formel in die folgende ändern.


Alles ist sehr einfach. In diesem Fall wäre drei Uhr ein Null-Grad-Winkel, sechs Uhr wäre ein 90-Grad-Winkel, neun Uhr wäre ein 180-Grad-Winkel und zwölf Uhr wäre ein 270-Grad-Winkel. Es sieht zwar so aus, als hätten wir nur den Winkelwert von Null auf 180 Grad geändert, aber wir haben noch etwas anderes getan: Die Drehrichtung hat sich nun von gegen den Uhrzeigersinn in den Uhrzeigersinn geändert.

Aber das ist nicht das, was ich zeigen wollte. Das ist einfach ein interessanter Punkt. Um wirklich zu erklären, was ich zeigen möchte, sollten wir zunächst alles in der Form darstellen, die jeder normalerweise verwendet. Eben weil es in vielen Fällen einfacher und leichter ist. Dies ist jedoch etwas, das wir nicht einfach an jede Situation anpassen können.

Wir werden jetzt nicht im Detail darauf eingehen, da der Schwerpunkt des Artikels nicht darauf liegt, sondern auf der Modellierung von Matrixberechnungen. Schauen wir uns an, wie Programmierer diese Probleme normalerweise lösen. Am einfachsten ist es, die Dinge, für die wir uns interessieren, in virtuelle Einheiten zu verwandeln. Wir führen zunächst alle Berechnungen durch und rechnen dann das Ergebnis in grafische Maßeinheiten um. Wenn wir etwas mit virtuellen Einheiten erstellen, verwenden wir Fließkommazahlen, in der Regel vom Typ double, und keine ganzen Zahlen. Der gleiche Code, den wir zuvor gesehen haben, würde also wie folgt aussehen:

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

Es hat sich fast nichts geändert, außer dass wir jetzt in das erstellte Objekt hineinzoomen können. Dies geschieht in den Zeilen 20 und 21. Das Objekt wird in den Zeilen 14 und 15 erstellt. Der gesamte Code bleibt praktisch unverändert. Diese einfache Änderung kann jedoch dazu beitragen, die Anwendung auf andere Szenarien zu übertragen. Wenn der Nutzer zum Beispiel die Größe des Charts ändert, können Sie nur den Größenparameter in Zeile 12 ändern, um die Objekte in einem bestimmten Verhältnis zu halten.

Aber ich verstehe immer noch nicht, wie mir das bei der Arbeit mit Matrizen helfen soll. Wenn ich es so machen kann, warum sollte ich es verkomplizieren? Schauen wir mal, ob es wirklich schwieriger ist, solche Dinge mit Matrizen zu machen. Hierfür werden wir ein neues Thema eröffnen.


Jetzt mit Matrixfaktorisierung

Da der Zweck dieses Artikels darin besteht, den Leser aufzuklären, werde ich versuchen, die Dinge so einfach wie möglich zu halten. Daher implementieren wir nur das, was notwendig ist, um das benötigte Material zu demonstrieren, d. h. die Matrixmultiplikation in diesem speziellen Fall. Aber Sie, liebe Leserinnen und Leser, werden sehen, dass dies allein schon ausreicht, um eine Matrix-Skalar-Multiplikation durchzuführen. Die größte Schwierigkeit, auf die viele Menschen bei der Implementierung von Code mit Matrixfaktorisierung stoßen, ist folgende: Im Gegensatz zur skalaren Faktorisierung, bei der in fast allen Fällen die Reihenfolge der Faktoren das Ergebnis nicht verändert, ist dies bei der Verwendung von Matrizen nicht der Fall. Wie würde der gleiche Code wie im vorherigen Thema aussehen, wenn wir Matrizen verwenden würden? Nachfolgend finden Sie den Code, in dem die Faktorisierung mit Hilfe von Matrizen erfolgt.
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. //+------------------------------------------------------------------+

Wir haben den Code so einfach wie möglich gehalten, ohne besondere Änderungen, damit Sie, liebe Leser, ihn verstehen können. Wenn ich über die Faktorisierung mit Hilfe von Matrixberechnungen schreibe, gehe ich normalerweise etwas anders vor, aber darüber werden wir in zukünftigen Artikeln sprechen, da einige Details erklärt werden müssen. Daher muss ich diese Details näher erläutern, und auch wenn es einfach erscheinen mag, werden Sie sich in einem bodenlosen Loch wiederfinden, wenn Sie sich nicht richtig darum kümmern. Aber was bewirkt dieser verrückte Code überhaupt? Sie tut dasselbe wie im vorherigen Thema, nur dass wir hier die Matrixmultiplikation verwenden, um die gleichen Berechnungen durchzuführen. Dieser Code mag Ihnen sehr kompliziert erscheinen, aber nein, er ist viel einfacher als der vorherige. Alles ist viel einfacher, wenn man weiß, wie man eine Matrix mit einer anderen multipliziert. Die Multiplikation erfolgt jedoch nicht ganz wie erwartet. Mit anderen Worten, wir können einige Dinge nicht tun, wenn wir den Code ändern müssen. Aber wie ich bereits sagte, dient diese Version nur zu Demonstrationszwecken. Die richtige Option wird im nächsten Artikel vorgestellt. Die Faktorisierung wird jedoch das erwartete Ergebnis liefern, auch wenn sie auf eine etwas seltsame Weise durchgeführt wird.

Sie sollten dieses Detail kennen, bevor Sie es verwenden. Aber wenn Sie bereits eine Vorstellung davon haben, wie die Matrixfaktorisierung funktioniert, wird der Rest viel einfacher zu verstehen sein. Beachten Sie, dass Zeile 12 den Code implementiert, der die Matrixfaktorisierung durchführt. Etwas Einfaches. Im Grunde multiplizieren wir die erste Matrix mit der zweiten und setzen das Ergebnis in die dritte. Da die Multiplikation für die Spalten einer Matrix mit den Zeilen einer anderen durchgeführt wird, ist die Faktorisierung recht einfach. Dabei sind einige Regeln zu beachten, z. B. muss die Anzahl der Zeilen in der einen Matrix gleich der Anzahl der Spalten in der anderen sein. Es handelt sich hierbei um grundlegende Mathematik über Matrizen. Versuchen Sie also, diese zuerst zu verstehen, wenn Sie nicht wissen, wie Sie vorgehen sollen oder warum es so gemacht werden muss. In Matrix A platzieren wir Spalten, in Matrix B Zeilen. In unserem Fall erhalten wir ein Ergebnis, das dem unten dargestellten entspricht.

Ich weiß, dass das alles auf den ersten Blick sehr verwirrend erscheinen mag, aber es ist alles sehr einfach. Die Elementematrix A muss mit der Elementematrix B multipliziert werden. Als Ergebnis erhält man eine Elementematrix R. Im Grunde genommen ist es so:
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)
und so weiter. Dies ist genau das, was getan werden würde, wenn die Berechnungen skalar wären. Das ist genau das, was das Verfahren in Zeile 12 tut. Ich denke, es ist wichtig, dass wir bei der Konstruktion unserer Berechnungen viel mehr Freiheit haben. Um dies zu verstehen, schauen wir uns die Funktion in Zeile 21 an, wo der Pfeil konstruiert und dargestellt wird. Hier werden 5 Matrizen definiert. Wir könnten weniger brauchen. Lassen Sie uns das erst einmal klarstellen. In Zukunft werden wir uns vielleicht überlegen, wie wir dies auf eine viel intuitivere und verständlichere Art und Weise modellieren können, aber selbst in diesem Modell ist es noch ziemlich einfach zu verstehen. Zwischen den Zeilen 23 und 33 zeichnen wir unsere Matrizen, die wir in den Berechnungen verwenden werden. In Zeile 35 deklarieren wir die Elemente, die wir zur Darstellung des Pfeils auf dem Bildschirm verwenden werden. Zwischen den Zeilen 37 und 40 führen wir die erforderlichen Berechnungen durch. Und jetzt aufgepasst: Was wir tun werden, ist sehr interessant. Sowohl aus praktischer Sicht als auch unter dem Gesichtspunkt der Faktorisierungsfreiheit. In Zeile 37 wird zunächst die Matrix M_1 mit der Matrix M_2 multipliziert, und das Ergebnis geht in die Matrix M_3 ein. Was ergibt diese Multiplikation? Das Bild wird gedreht. Wir können sie jedoch noch nicht auf dem Bildschirm anzeigen, da die Skala noch im Modell verwendet wird, das sich in der Matrix M_2 befindet. Um das Bild im richtigen Maßstab darzustellen, muss eine weitere Berechnung durchgeführt werden. Aber zuerst löschen wir in Zeile 38 den Inhalt der Matrix M_1, und gleich danach, in Zeile 39, geben wir an, wie der Maßstab des Bildes geändert werden soll. Wir haben im Moment einige neue Optionen, aber wir werden uns nur auf die Skalierung konzentrieren. In Zeile 40 multiplizieren wir die bereits gedrehten Objektdaten mit einer neuen Matrix, die bereits den gewünschten Maßstab enthält. Dies ist im Folgenden dargestellt.

Dabei ist Sx die neue Dimension entlang der X-Achse und Sy die neue Dimension entlang der Y-Achse. Da wir beide Werte auf „Größe“ setzen, werden die Änderungen so vorgenommen, als würden wir das Objekt heranzoomen. Deshalb habe ich gesagt, dass wir viel mehr tun können. Da die Funktion FillPolygon nicht weiß, wie sie mit dieser Matrixstruktur arbeiten soll, müssen wir die Daten aufteilen, damit FillPolygon den Pfeil für uns zeichnen kann. Wir haben eine for-Schleife in Zeile 48 und das Ergebnis ist das gleiche wie im vorherigen Thema.


Abschließende Überlegungen

In diesem Artikel habe ich eine recht einfache Methode für die Arbeit mit Matrixberechnungen vorgestellt. Ich habe versucht, etwas Interessantes zu präsentieren und gleichzeitig einen Weg zu finden, auf einfachste Weise zu erklären, wie diese Berechnung durchgeführt wird. Das in diesem Artikel betrachtete Matrixmodell ist jedoch nicht das am besten geeignete, um alle möglichen Arten der Faktorisierung durchzuführen. Deshalb werde ich bald einen neuen Artikel veröffentlichen, in dem ich über ein geeigneteres Modell zur Darstellung von Matrizen sprechen werde. Ich werde auch eine Erklärung geben, damit Sie diese neue Datenfaktorisierungsmethode anwenden können. Bis bald im nächsten Artikel.


Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/13646

Beigefügte Dateien |
App1.mq5 (1.87 KB)
App2.mq5 (1.97 KB)
App3.mq5 (2.62 KB)
DoEasy. Dienstfunktionen (Teil 2): Das Muster der „Inside-Bar“ DoEasy. Dienstfunktionen (Teil 2): Das Muster der „Inside-Bar“
In diesem Artikel werden wir uns weiter mit den Preismustern in der DoEasy-Bibliothek beschäftigen. Wir werden auch die Klasse für das Muster der „Inside-Bar“ der Price Action Formationen erstellen.
Entwicklung eines Replay Systems (Teil 45): Chart Trade Projekt (IV) Entwicklung eines Replay Systems (Teil 45): Chart Trade Projekt (IV)
Der Hauptzweck dieses Artikels ist die Einführung und Erläuterung der Klasse C_ChartFloatingRAD. Wir haben einen Chart Trade-Indikator, der auf recht interessante Weise funktioniert. Wie Sie vielleicht bemerkt haben, haben wir immer noch eine relativ kleine Anzahl von Objekten im Chart, und dennoch erhalten wir die erwartete Funktionalität. Die im Indikator enthaltenen Werte können bearbeitet werden. Die Frage ist, wie ist das möglich? Dieser Artikel wird die Dinge etwas klarer machen.
Zeitreihen-Clustering für kausales Schlussfolgern Zeitreihen-Clustering für kausales Schlussfolgern
Clustering-Algorithmen beim maschinellen Lernen sind wichtige unüberwachte Lernalgorithmen, die die ursprünglichen Daten in Gruppen mit ähnlichen Beobachtungen unterteilen können. Anhand dieser Gruppen können Sie den Markt für ein bestimmtes Cluster analysieren, anhand neuer Daten nach den stabilsten Clustern suchen und kausale Schlüsse ziehen. In dem Artikel wird eine originelle Methode für das Clustering von Zeitreihen in Python vorgeschlagen.
Entwicklung eines Replay Systems (Teil 44): Chart Trade Projekt (III) Entwicklung eines Replay Systems (Teil 44): Chart Trade Projekt (III)
Im vorherigen Artikel habe ich erklärt, wie Sie Vorlagedaten zur Verwendung in OBJ_CHART manipulieren können. In diesem Artikel habe ich das Thema nur umrissen, ohne auf Einzelheiten einzugehen, da die Arbeit in dieser Version sehr vereinfacht war. Dies geschah, um die Erklärung des Inhalts zu erleichtern, denn trotz der scheinbaren Einfachheit vieler Dinge waren einige davon nicht so offensichtlich, und ohne das Verständnis des einfachsten und grundlegendsten Teils wäre man nicht in der Lage, das gesamte Bild wirklich zu verstehen.