Combinatorica in MQL5

[English version] [MQLmagazine.com in english] [Editia romaneasca]

Cu mult timp in urma am scris pe fostul meu blog despre generarea permutarilor, dar nu m-am scufundat destul de mult in explicarea algoritmului si a codului. Intr-adevar, exista situatii cand combinatorica trebuie sa fie aplicata – cand cautam arbitraje sau pentru statistici care au ca cerinta selectia activelor.

Permutari

Permutarile sunt destul de simplu de gasit si de generat, desi manualele de matematica sar peste acesti algoritmi. Daca permutarile sunt ordine specifice in care elementele unei multimi sunt aranjate, aceasta inseamna ca in timp ce aranjam elementele, posibilitatile de a aranja elementele ramase se reduc. Daca avem 4 elemente intr-o multime, dupa ce-l asezam pe primul mai ramand doar 3 locuri disponibile, si asa mai departe pana cand ultimul element se aseaza automat in singurul loc liber ramas. De aceea, compozitia numarului care da permutarea se face ca la compuneerea unui numar intr-o alta baza de numeratie, cu diferenta ca baza de numeratie se micsoreaza la fiecare pas.

De exemplu avem de gasit numarul urmatoarei permutari:

|1|2|3|4|
|1|4|2|3|

La inceput, baza de numeratie este 4, iar tabloul nostru este gol:

|1|2|3|4| : notatia matematica a pozitiilor
|1|4|2|3| : permutarea de descifrat

|0|0|0|0| : locuri ocupate in notatia pozitiilor, in prezent
|0|1|2|3| : locuri in tablou (de la zero)
|0|1|2|3| : tablou de numarare

Ne uitam la prima pozitie, 1, si citim primul element, “1″. Din ratiuni de computing folosim tablouri bazate pe zero.
Numarul permutarii = 1-1 = 0

|1|2|3|4| : notatia matematica a pozitiilor
|1|4|2|3| : permutarea de descifrat

|0|0|0|0| : locuri ocupate in notatia pozitiilor, in prezent
|1|0|0|0| : locuri ocupate in notatia pozitiilor dupa inserare
|0|1|2|3| : tabloul de numarare (conceptual, doar pentru numarare)
|x|0|1|2| : noul tablou de numarare pentru elementele ramase.

Acum ne uitam la al doilea element. Al doilea element este “4″. Acum, pentru ca mai avem doar 3 locuri libere, baza de numeratie este 3. Numarul permutarii = (0 x 3) = 0. La aceasta, adaugam pozitia in tabloul de numarare pentru elementul “4″, care este 2 : Numarul permutarii = 0 + 2 = 2

|1|2|3|4| : notatia matematica a pozitiilor
|1|4|2|3| : permutarea de descifrat

|1|0|0|0| : locuri ocupate in notatia pozitiilor, in prezent
|1|0|0|4| : locuri ocupate in notatia pozitiilor, dupa inserare
|x|0|1|2| : tabloul de numarare
|x|0|1|x| : noul tablou de numarare pentru elementele ramase

Acum ne uitam la al treilea element. Al treilea element este “2″. Acum cand avem 2 locuri libere, baza de numeratie este 2.
Numarul de permutare = (2 x 2) = 4. La acesta, adaugam pozitia in tabloul de numarare pentru elementul “2″, care este 0 : Numarul de permutare = 4 + 0 = 4

|1|2|3|4| : notatia matematica a pozitiilor
|1|4|2|3| : permutarea de descifrat

|1|0|0|4| : locuri ocupate in notatia pozitiilor, in prezent
|1|2|0|4| : locuri ocupate in notatia pozitiilor, dupa inserare
|x|0|1|2| : tablou de numarare
|x|x|0|x| : noul tablou de numarare pentru elementele ramase.

Calculul se opreste. Avem de inserat numai n-1 elemente. Al n-lea element este prezumat a se afla in locul ramas liber.

Numarul de permutare final = 4

Algoritmul invers, de generare a permutarilor din numerele de permutare, este urmatorul:
Incepand de la baza 2, efectuam n-1 pasi de divizune la o baza in crestere, pastrand restul.
Astfel avem
4 / 2 = [2], remainder 0
[2] / 3 = 0, remainder 2
[0] / 4 = 0, remainder 0

Apoi incepem sa construim permutarea la loc. Citim ultimul rest, pe care il gasim a fi 0. Aceasta inseamna ca primul element este intr-adevar “1″, pentru ca 0 din tabloul de numarare este 1 in tabloul pozitiilor.

|1|2|3|4| : notatia matematica a pozitiilor
|0|1|2|3| : subscripturi fizice in variabila tablou
|0|1|2|3| : tablou de numarare
|1|0|0|0| : pozitii ocupate dupa inserarea elementului curent
|x|0|1|2| : tablou de numarare dupa inserarea elementului curent
|1|0|0|0| : permutarea construita

Baza s-a micsorat la 3, pentru ca mai avem 3 locuri ramase. Citim restul urmator, in ordine inversa, si gasim ca este 2. Uitandu-ne in noul tabloul de numarare, vedem ca pozitia 2 corespunde elementului “4″ din notatia matematica.

|1|2|3|4| : notatia matematica a pozitiilor
|0|1|2|3| : subscripturi fizice in variabila tablou
|x|0|1|2| : tablou de numarare
|1|0|0|4| : pozitii ocupate dupa inserarea elementului curent
|x|0|1|x| : tablou de numarare dupa inserarea elementului curent
|1|0|0|4| : permutarea construita

Baza s-a micsorat la 2, caci mai avem 2 locuri libere. Citim restul urmator, in ordine inversa, si gasim ca este 0. Uitandu-ne in noul tablou de numarare, vedem ca pozitia 0 corespunde elementului “2″ din notatia matematica.

|1|2|3|4| : notatia matematica a pozitiilor
|0|1|2|3| : subscripturi fizice in variabila tablou
|x|0|1|x| : tablou de numarare
|1|2|0|4| : pozitii ocupate dupa inserarea elementului curent
|x|x|0|x| : tablou de numarare dupa inserarea elementului curent
|1|4|0|2| : permutarea construita

Mai avem un singur element de determinat, si acesta este “3″, pe singurul loc ramas neocupat.

|1|2|3|4| : notatia matematica a pozitiilor
|0|1|2|3| : subscripturi fizice in variabila tablou
|x|x|0|x| : tablou de numarare
|1|2|3|4| : pozitii ocupate dupa inserarea elementului curent
|x|x|x|x| : tablou de numarare dupa inserarea elementului curent
|1|4|3|2| : permutarea construita

Pentru ca ambii algoritmi sunt liniari, ei poti fi implementati direct in functii. Astfel, incepem sa scriem Combinatorics.mqh.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//+------------------------------------------------------------------+
//|                                                Combinatorics.mqh |
//|                                       Copyright Bogdan Caramalac |
//|                                           http://mqlmagazine.com |
//+------------------------------------------------------------------+
#property copyright "Copyright Bogdan Caramalac"
#property link      "http://mqlmagazine.com"
 
//*************************************************
 
 string Replicate(string s, int count)
   {
    string res="";
    if (count<=0)
      return(res);
    for (int i=1;i<=count;i++)
      StringConcatenate(res,res,s);
    return(res);
   }
 
 int MathDiv(int a,int b)
    {
     int res;
     res=a-MathRound(MathMod(a,b));
     res=MathRound(res/b);
     return(res);
    }
 
 int Round(double a)
    {
     return(MathRound(NormalizeDouble(a,0)));
    }
 
//*************************************************

Lucruri obisnuite pe care nu insistam, mergem mai departe spre Permutari:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//********************************************************************
//
//
//                             Permutations
//
//
//********************************************************************
 
 long Factorial(ushort n)
   {
    long res=1;
    for (int i=2;i<=n;i++)
       res=res*i;
    return(res);
   }
 
 int FindPermutation(ushort elem, int& perm[])
    {
     int res=0;
     int rr;
     int positions[30];
     for (int i=0;i<ArrayRange(positions,0);i++)
        positions[i]=i+1;
     for (int e=1;e<elem;e++)//we don`t look for the last element , there is one seat left
        {
         rr=0;
         for (int i=0;i<elem;i++)
            {
             if (perm[e-1]==i+1)
               {
                res=res+rr;//has already -1, it`s zero-based
                //Print("Found element ",perm[e-1]," to be on perm[",i,"]. Adding ",rr, " to res, equals ",res);
                if (e!=elem-1)
                  res=res*(elem-e);
                //Print("Multiplicating by elem-e (",elem-e,") = ",res);                
                positions[i]=-1;
               }
             else
               { //if the searched element is not on current element,
                 //increment place counter only current position was not previously used;
                if (positions[i]!=-1)
                  rr++;
               }
            }
        }//for (int e=1;i<=elem;i++)
      return(res);  
    }
 
void GeneratePermutation(int number,int elem,int &perm[])
    {    
    string elemallow; 
    int stoppoint,crtpoint;       
    ushort AscX=StringGetCharacter("X",0);
    int i,j,base;
    int nnow;
    elemallow=Replicate("a",20);
    base=2;    
    nnow=number;    
    for (i=elem-2;i>=0;i--)
       {
       perm[i]=Round(MathMod(nnow,base));       
       nnow=MathDiv(nnow,base);
       base=base+1;        
       }
    perm[0]=perm[0]+1;//because first element is from 1 to elem, not 0 to elem-1    
    for (i=0;i<=elem-2;i++)
       {
       if (i==0)
         {
          StringSetCharacter(elemallow,perm[0]-1,AscX);
         }             
       else
          {
          stoppoint=perm[i]+1;          
          crtpoint=0;
          for (j=0;j<elem;j++)
             {             
             if (StringGetCharacter(elemallow,j)==AscX)
                continue;
             else
                {
                crtpoint=crtpoint+1;
                if (crtpoint==stoppoint)
                   {
                   perm[i]=j+1;//write new perm[i]
                   StringSetCharacter(elemallow,j,AscX);
                   }                   
                }
             }//for (j=0;j<elem;j++);
          }//else if (i=0)    
        }//for (i=0;i<elem-2;i++)
    for (i=1;i<=elem;i++)
       {
       if (StringGetCharacter(elemallow,i-1)!=AscX)
          {          
          perm[elem-1]=i;          
          break;
          }
       }        
    return;  
    }

De data asta am avut noroc. Doar doua functii simple de apelat. Nu va fi si cazul Combinarilor.

Combinations

Algoritmul de combunari este mult mai greu de inteles decat algoritmul de permutari. Cel mai folosit algoritm este cel care genereaza combinarile intr-o manevra recursiva.

Sa ne uitam la urmatoarea serie de combinari: C(5,3)

1, 2, 3
1, 2, 4
1, 2, 5
1, 3, 4
1, 3, 5
1, 4, 5

Mai intai, fiecare element are un grad de libertate diferit. De exemplu, prmul nu poate fi dincolo de n-k+1.
Alt lucru, dupa ce generezi prmul element, ceea ce ai de facut sunt C(n-1,k-1).
De exemplu, C(4,2) sunt:

1, 2
1, 3
1, 4
2, 3
2, 4
3, 4

Pentru fiecare dintre acestea, C(3,1) sunt:

1
2
3

Acum poti vedea cum se integreaza:

Cand procedura este apelata pentru a genera C(5,3) incepe prina a enumera primul element, de la 1 la 3. Astfel, are:

1,(1+x)

Apoi se autoapeleaza pentru a genera C(4,2) , si enumereaza de la 1 la 2.

1,(1+1),(2+x)

In final se autoapeleaza pentru a genera C(3,1) si completeaza:

1,2,(2+1) = 1,2,3
1,2,(2+2) = 1,2,4
1,2,(2+3) = 1,2,5

Se intoarce la ciclul urmator si continua cu urmatorul element din C(4,2) si anume 2:

1,(1+2),(1+2+x)

Apeleaza apoi C(3,1) si completeaza:

1,3,4
1,3,5

Se intoarce apoi la C(5,3) si continua sa enumereze urmatorul element, 2.

Algoritmul continua pana cand prima apelare incheie ciclul de enumerare.

Implementarea algoritmului e destul de intunecoasa. De aceasta data am optat pentru o clasa. Deoarece, daca generarea directa nu este posibila, nu e fezabil sa generezi combinatiile de fiecare data cand vrei o anumita combinatie, in cazul in care poti dori mai multe dintr-o lista. Asadar, e nevoie de o clasa, care aduce un loop interior de generare a combinatiilor in timp ce apeleaza o functie callback care sa-i dea utilizatorului combinatiile gasite, cu optiunea de terminare anticipata.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//********************************************************************
//
//
//                             Combinations
//
//
//********************************************************************
 
 
long CombinationsTotal(ushort n,ushort k)
  {
   return(Factorial(n)/(Factorial(k)*Factorial(n-k)));
  }
 
class CombinationObject
  {   
   protected: 
   long counter;
   ushort n,k;
   bool terminate;   
   ushort current_combination[];
   private:
   ushort last_combination[];
   bool found;
 
   long CombinationsTotal(ushort n,ushort k);
   string CombImage(ushort &ccomb[],ushort n);
   void GenCombinationsRec(ushort depth,ushort nn,ushort kk);
 
   public:
   void SetupCombinationObject(ushort nn,ushort kk); 
   void LoopCombinations();
   virtual void CombinationsCallback();
   CombinationObject() {SetupCombinationObject(6,3);};   
  };
 
void CombinationObject::CombinationsCallback()
  {
   Print(CombImage(current_combination,k));
  }
 
void CombinationObject::SetupCombinationObject(ushort nn,ushort kk)
  {
   n=nn;
   k=kk;
   terminate=false;
   ArrayInitialize(current_combination,0);
   ArrayInitialize(last_combination,0);
   ArrayResize(current_combination,n);
   ArrayResize(last_combination,n);
   counter=0;
  }
 
string CombinationObject::CombImage(ushort &ccomb[],ushort n)
  {
   string res="";
   for (int i=0;i<n;i++)
      res=res+DoubleToString(ccomb[i],0)+" ";
   return(res);
  }
 
void CombinationObject::GenCombinationsRec(ushort depth,ushort nn, ushort kk)
  {
   bool okay,r;
   if (terminate==true)
     return;
 
   if (depth==k)
     return;  
 
   if (kk==0)
     return;
 
   int allows=nn-kk+1; 
   for (ushort i=1;i<=allows;i++)
      {
       if (terminate==true)
          return;
       if (depth==0)
         current_combination[depth]=i;
       else
         current_combination[depth]=current_combination[depth-1]+i;
 
       GenCombinationsRec(depth+1,nn-i,kk-1);
 
       okay=false;//checking for identical combination
       for (int j=1;j<=k;j++)
          {
           if (current_combination[j-1]!=last_combination[j-1])
             okay=true;//okay, new one
          }
       if (okay==false)
         continue;
       else
         { //copying current to last
          for (int j=1;j<=k;j++)
             last_combination[j-1]=current_combination[j-1];
         }
 
       CombinationsCallback();
       if (terminate==true)
         return;      
       counter=counter+1;
 
      }//for (int i=1;i<=allows;i++)
   return;
  }
 
void CombinationObject::LoopCombinations()
  {
   GenCombinationsRec(0,n,k);
   return;
  }

Asta e o nuca mai greu de spart. Functia interesanta aici, motorul in sine, este GenCombinationsRec(). Parametrul depth spune functiei la ce nivel incepe generarea combinarilor. Cand depth este epuizat, adica la sfarsitul fiecarei cozi de apelare, o noua combinare este generata in current_combination[] si contorul este incrementat. Ceea ce are userul de facut, e sa mosteneasca aceasta clasa intr-o clasa proprie si sa supraincarce CombinationsCallback(). De asemenea, variabila terminate este disponibila, iar daca e setata la true, odata ce CombinationsCallback() termina, toate apelurile GenCombinationsRec() se vor incheia inainte de vreme si programul isi va relua executia.

Arrangements

Intrucat aranjamentele sunt combinatii permutate, nu e niciun rost de a complica problema. Clasa de aceasta data e aproape o copie a clasei CombinationObject, cu diferenta ca metoda recursiva aici, GenArrangementsRec(), este ca si GenCombinationsRec(), genereaza combinari, si apoi, cand fiecare dintre ele este gata, le permuteaza si serveste fiecare aranjament (combinatie permutata) catre ArrangementsCallback(). Simplu si convenabil, dar rezultatele nu sunt chiar in maniera sortata ca la Permutari si Combinari:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//********************************************************************
//
//
//                             Arrangements
//
//
//********************************************************************  
 
long ArrangementsTotal(ushort n,ushort k)
  {
   return(Factorial(n)/Factorial(n-k));
  }
 
class ArrangementObject
  {
   protected: 
   long counter;
   ushort n,k;
   ushort current_arrangement[];
   bool terminate;  
   private:
   ushort current_combination[];  
   ushort last_combination[];     
   bool found;
 
   long ArrangementsTotal(ushort n,ushort k);
   string ArrgImage(ushort &ccomb[],ushort n);
   void GenArrangementsRec(ushort depth,ushort nn,ushort kk);
 
 
   public:
   void SetupArrangementObject(ushort nn,ushort kk); 
   void LoopArrangements();
   virtual void ArrangementsCallback();
   ArrangementObject() {SetupArrangementObject(6,3);};   
  };
 
void ArrangementObject::ArrangementsCallback()
  {
   Print(ArrgImage(current_arrangement,k));
  }
 
void ArrangementObject::SetupArrangementObject(ushort nn,ushort kk)
  {
   n=nn;
   k=kk;
   terminate=false;   
   ArrayInitialize(current_arrangement,0);
   ArrayInitialize(current_combination,0);
   ArrayInitialize(last_combination,0);
   ArrayResize(current_combination,n);
   ArrayResize(current_arrangement,n);
   ArrayResize(last_combination,n);   
   counter=0;
  }
 
 
 
string ArrangementObject::ArrgImage(ushort &ccomb[],ushort n)
  {
   string res="";
   for (int i=0;i<n;i++)
      res=res+DoubleToString(ccomb[i],0)+" ";
   return(res);
  }
 
void ArrangementObject::GenArrangementsRec(ushort depth,ushort nn, ushort kk)
  {
   int perm[30];
   bool okay,r;
   if (terminate==true)
     return;
 
   if (depth==k)
     return;  
 
   if (kk==0)
     return;
 
   int allows=nn-kk+1; 
   for (ushort i=1;i<=allows;i++)
      {
       if (terminate==true)
          return;
       if (depth==0)
         current_combination[depth]=i;
       else
         current_combination[depth]=current_combination[depth-1]+i;
 
       GenArrangementsRec(depth+1,nn-i,kk-1);
 
       okay=false;//checking for identical combination
       for (int j=1;j<=k;j++)
          {
           if (current_combination[j-1]!=last_combination[j-1])
             okay=true;//okay, new one
          }
       if (okay==false)
         continue;
       else
         { //copying current to last
          for (int j=1;j<=k;j++)
             last_combination[j-1]=current_combination[j-1];
         }
 
 
       //here combination is final and arrangements are generated       
       for (int iperm=0;iperm<Factorial(k);iperm++)
          {
           GeneratePermutation(iperm,k,perm);           
           for (int jperm=0;jperm<k;jperm++)
              {
               current_arrangement[jperm]=current_combination[perm[jperm]-1];                              
              }                      
           ArrangementsCallback();                        
           if (terminate==true)
             return;      
           counter=counter+1;   
          }   
 
       }//for (int i=1;i<=allows;i++)
   return;                   
  }
 
void ArrangementObject::LoopArrangements()
  {
   GenArrangementsRec(0,n,k);
   return;
  }

Si acum un script demonstrativ (combtest.mq5). Scriptul va afisa aranjamente de n perechi forex luate cate k, cu un k in crestere, de la 3 la n. Perechile forex sunt extrase la inceput, interogand fiecare instrument despre modul lui de calcul si retinand numai cele care raspund SYMBOL_CALC_MODE_FOREX, cu specificatia ca simbolul poate avea doar 6 sau mai multe litere (doua perechi forex si un sufix). Perechile sunt extrase si adaugate intr-un tablou. Apoi un motor customizat de aranjamente este apelat pentru a afisa aranjamentele:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//+------------------------------------------------------------------+
//|                                                     combtest.mq5 |
//|                                       Copyright Bogdan Caramalac |
//|                                           http://mqlmagazine.com |
//+------------------------------------------------------------------+
#property copyright "Copyright Bogdan Caramalac"
#property link      "http://mqlmagazine.com"
#property version   "1.00"
 
 
#include <Combinatorics.mqh>
 
int CurrenciesCount;
int PairsCount;
string ForexPairs[100];
 
//our class: we needed it to override ArrangementsCallback;
class LocalArrgClass : public ArrangementObject
  { 
    void ArrangementsCallback()
     {
      string image="";
      image=DoubleToString(counter,0)+" :";
      for (int i=0;i<k;i++)         
         image=image+" "+ForexPairs[current_arrangement[i]-1];
      Print(image);      
     }   
  };
 
//the arrangment object
LocalArrgClass myarrg;
 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
 
void CheckAndAddPair(string pair)
  {
   bool found;
   if (PairsCount==0)
     {
      ForexPairs[PairsCount]=pair;
      PairsCount++;
     }
   else
     {
      found=false;
      for (int i=0;i<PairsCount;i++)
         {
          if (ForexPairs[i]==pair)
            {
             found=true;
             break;
            }          
         }
      if (found==false)
        {
         ForexPairs[PairsCount]=pair;
         PairsCount++;               
        } 
     } 
   return;
  }
 
void OnStart()
  {  
   string crtsymbol,pair1,pair2;
   CurrenciesCount=0;
   PairsCount=0;
   for (int i=0;i<SymbolsTotal(false);i++)
      {
       crtsymbol=SymbolName(i,false);
       if (SymbolInfoInteger(crtsymbol,SYMBOL_TRADE_CALC_MODE)==SYMBOL_CALC_MODE_FOREX&&StringLen(crtsymbol)>=6)
         {          
          pair1=StringSubstr(crtsymbol,0,3);          
          CheckAndAddPair(pair1);
          pair2=StringSubstr(crtsymbol,3,3);         
          CheckAndAddPair(pair2);
         }
      }//for (int i=0;i<SymbolsTotal(false);i++)        
    for (int iarrg=3;iarrg<=PairsCount;iarrg++)
       {
        myarrg.SetupArrangementObject(PairsCount,iarrg);
        myarrg.LoopArrangements();      
       }
  }
//+------------------------------------------------------------------+

Cam asa arata (desi trebuie oprit cu ajutorul lui terminate – altfel va sari o parte din jurnal):

Ai putea intreba de ce am optat pentru ca permutarile sa foloseasca int in loc de uint sau long. Raspunsul e problema legata de MathMod() si MathRound(). Aceste functii nu lucreaza cu tipuri intregi. Chiar daca asteapta si intorc intregi ca si inteles ele inca lucreaza cu double ca si tip. Asa ca toate convertirile trebuie sa se bazeze pe un cast fortat al unui double, care e un real cu semn, asa cum int e un intreg cu semn. Folosirea intregilor fara semn sau chiar long care se intinde pe 8 bytes in loc de 6 ca double ar fi complicat problema. Cred ca functiile trebuie sa fie refacute de MetaQuotes, pentru a lucra numai cu timpuri intregi si pentru a se supune tipurilor intregi primite care joaca rol de operanzi, tot asa cum div si mod lucreaza in implementarile Pascal. Iar in legatura cu MathRound(), aceasta trebuie sa fie o functie reala de rotunjire care intoarce un intreg conform tipului care asteapta rezultatul, sau intr-un tip intreg generic, cand e folosita in expresii.

Linkuri:
Combinatorics.mqh
combtest.mq5

Editii