Genel sınıflar kütüphanesi - hatalar, açıklamalar, sorular, kullanım özellikleri ve öneriler - sayfa 18

 

Bunu başlattı

 #include <Generic\ArrayList.mqh>
#include <Generic\HashMap.mqh>

CHashMap< int , int > MagicsByDeals;

#define AMOUNT 1000000

template < typename T>
int Test( T &Object )
{
   int Res = 0 ;
  
   for ( int i = 0 ; i < AMOUNT; i++)
  {
     int Tmp = 0 ;
    
     if (Object.TryGetValue(i, Tmp))    
      Res += Tmp;
  }
  
   return (Res);
}

#define BENCH(A)                                                              \
{                                                                             \
   const ulong StartTime = GetMicrosecondCount ();                              \
  A;                                                                          \
   Print ( "Time[" + #A + "] = " + ( string )( GetMicrosecondCount () - StartTime)); \
} 

void OnStart ()
{   
  CArrayList< int > CollectionList;
  CHashMap< int , int > CollectionHash;
  
   for ( int i = 0 ; i < AMOUNT; i++)
  {      
    CollectionHash.Add(i, i);
    CollectionList.Add(i);
  }
  
  BENCH( Print (Test(CollectionList)));
  BENCH( Print (Test(CollectionHash)));
}


ve anladım

 1783293664
Time [ Print (Test(CollectionList))] = 24819
1783293664
Time [ Print (Test(CollectionHash))] = 91270


CarrayList, karma değişkenden daha hızlıdır. CArrayList'in iç kısımlarına tırmandı ve hemen orada

 template < typename T>
class CArrayList: public IList<T>
  {
protected :
   T                 m_items[];
   int                m_size;

Şimdi aldatılmış hissediyorum. CArrayList'in sıradan bir dizi etrafındaki sarmalayıcı olduğu ortaya çıktı. Önceki / Sonraki işaretçileriyle normal bir liste olduğunu düşündüm, ama işte burada

template<typename T>
bool CArrayList::TryGetValue( const int index,T & value )
  {
//--- check index
   if (( uint )index<( uint )m_size)
     {
       //--- get value by index
       value =m_items[index];
       return ( true );
     }
   return ( false );
  }
 
fxsaber :
Yapılarla çalışmak için gerçek algoritmalara ek olarak, görevin özelliklerine göre doğru kapsayıcıyı seçme sorunu da vardır.
 
birleştirici :
Yapılarla çalışmak için gerçek algoritmalara ek olarak, görevin özelliklerine göre doğru kapsayıcıyı seçme sorunu da vardır.

Yani aynı arayüz farklı uygulamalar için olabilir. Arayüzden değil, belirli uygulamadan - bir diziden - biraz hayal kırıklığı yaşadım. Karma ile karşılaştırıldığında - cennet ve dünya.

 
fxsaber :

Şimdi aldatılmış hissediyorum. CArrayList'in sıradan bir dizi etrafındaki sarmalayıcı olduğu ortaya çıktı. Önceki / Sonraki işaretçileriyle normal bir liste olduğunu düşündüm, ama işte burada


Ticaret, otomatik ticaret sistemleri ve ticaret stratejilerinin test edilmesi hakkında forum

Algoritmalar, karar yöntemleri, performanslarının karşılaştırılması

Sergey Dzyublik , 2017.12.10 20:52


Ne DBMS, veri yapılarında SIFIR'ı anlayan bir kişiye ne ovuşturuyorsunuz.
Böyle bir ArrayList (C++ 'dan vektör) kavramı yoksa ne hakkında konuşabiliriz .....


Daha çok senin dikkatsizliğin...
Mevcut tüm listeye göz atın - https://www.mql5.com/en/docs/standardlibrary/generic

CArrayList, C++'daki std::vector'a benzer
CLinkedList büyük olasılıkla C++'dan std::list'in bir analoğudur

 
fxsaber :

Bunu başlattı

ve anladım

CarrayList, karma değişkenden daha hızlıdır. CArrayList'in iç kısımlarına tırmandı ve hemen orada

Şimdi aldatılmış hissediyorum. CArrayList'in sıradan bir dizi etrafındaki sarmalayıcı olduğu ortaya çıktı. Önceki / Sonraki işaretçileriyle normal bir liste olduğunu düşündüm, ama işte burada

Tarihsel olarak, Liste bir sayfa değil, bir dizidir. Bu nedenle, her şey doğru. Bir sayfa genel olarak görünüyorsa, büyük olasılıkla LinkedList veya bunun gibi bir şey olarak adlandırılacaktır.

 
fxsaber :

Arayüzden değil, belirli uygulamadan - bir diziden - biraz hayal kırıklığı yaşadım.

bu kap bir dizidir (yani, C++'daki bir vektörün bir analoğudur) ve yerel bir mql dizisi çok iyidir, bu nedenle dizi listesi daha çok bir yığın gibidir, kullanımı biraz daha uygundur.
 

Ticaret, otomatik ticaret sistemleri ve ticaret stratejilerinin test edilmesi hakkında forum

Genel sınıf kitaplığı - hatalar, açıklama, sorular, kullanım ve öneriler

Sergey Dzyublik , 2017.12.09 01:12


Olası iyileştirme için birkaç öneri var:

1) Benim düşünceme göre, uygulama tamamen doğru olmayan bir kapasite seçimi içeriyor - hem ilk boyut 3 hem de hash tablosu yeniden oluşturulduğunda sonrakiler.
Evet, doğru, düzgün dağılım için bir asal sayı seçmeniz gerekiyor.
Ancak, CPrimeGenerator uygulaması beklentileri karşılamıyor ve eksik asal sayılar içeriyor.
Böyle bir "jeneratörün" amacı açıktır - otomatik asal sayılar oluşturma ile 1.2 mertebesinde bir büyüme faktörü sağlamak.
Ancak, bu çok küçük bir faktör değil mi? Vektörler için C++'da katsayı genellikle kitaplığa bağlı olarak 1.5-2.0'dır (çünkü işlemin ortalama karmaşıklığını büyük ölçüde etkiler).
Çıktı sabit bir katsayı olabilir, ardından bir asal sayılar listesinde ikili arama yoluyla sayı bir asal sayıya yuvarlanabilir .
Ve başlangıçtaki kapasite boyutu 3 çok küçük, geçen yüzyılda yaşamıyoruz.

2) Şu anda, karma tablosunun yeniden oluşturulması (Yeniden boyutlandırma) yalnızca %100 doldurma kapasitesi ile gerçekleştirilmektedir (tüm m_entries[] doldurularak)
Bu bağlamda, çok düzgün dağılıma sahip olmayan anahtarlar için önemli sayıda çarpışma mümkündür.
Bir seçenek olarak, karma tablo yeniden oluşturma işlemini gerçekleştirmek için gereken sınıra göre %100 azaltacak bir doldurma faktörü eklemeyi düşünün.

1) Hacim büyüme katsayısı (kapasite) 1.2'ye eşit değildir, CHashMap'te yeni hacmi hesaplamak için CPrimeGenerator::ExpandPrime yöntemi kullanılır:

 int CPrimeGenerator::ExpandPrime( const int old_size)
  {
   int new_size= 2 *old_size;
   if (( uint )new_size> INT_MAX && INT_MAX >old_size)
       return INT_MAX ;
   else
       return GetPrime(new_size);
  }

Bu yöntemde, koleksiyonun eski boyutu iki ile çarpılır, ardından ortaya çıkan değer için üstten en yakın asal sayıyı bulup yeni hacim olarak döndürürüz.

Kapasitenin başlangıç değeri hakkında - Katılıyorum, çok küçük.

Ancak diğer yandan, başlangıç hacmini açıkça belirtebileceğiniz yapıcılar her zaman vardır:

 class CHashMap: public IMap<TKey,TValue>
  {
public :
                     CHashMap( const int capacity );
                     CHashMap( const int capacity ,IEqualityComparer<TKey>*comparer);
  }

Bu nedenle, burada bir şeyi değiştirmenin pek bir anlamı görmüyorum.

2) Doldurma faktörü parametresini eklemek gerçekten de bazı durumlarda çarpışmaları önlemeye yardımcı olacaktır. Büyük ihtimalle uygulanacaktır.

 
Roman Konopelko :

1) Hacim büyüme katsayısı (kapasite) 1.2'ye eşit değildir, CHashMap'te yeni hacmi hesaplamak için CPrimeGenerator::ExpandPrime yöntemi kullanılır:

Bu yöntemde, koleksiyonun eski boyutu iki ile çarpılır, ardından ortaya çıkan değer için üstten en yakın asal sayıyı bulup yeni hacim olarak döndürürüz.

Kapasitenin başlangıç değeri hakkında - Katılıyorum, çok küçük.

Ancak diğer yandan, başlangıç hacmini açıkça belirtebileceğiniz yapıcılar her zaman vardır:

Bu nedenle, burada bir şeyi değiştirmenin pek bir anlamı görmüyorum.

2) Doldurma faktörü parametresini eklemek gerçekten de bazı durumlarda çarpışmaları önlemeye yardımcı olacaktır. Büyük ihtimalle uygulanacaktır.

Roman, bunu orada yaptığını sanmıyorum. Şimdi bazı genel sınıflar en gerekli yöntemleri bile içermiyor. Onları hiç denedin mi? Örneğin, kırmızı-siyah bir ağacın klasik bir uygulaması olan CRedBlackTree'yi ele alalım. Oldukça havalı bir algoritma, ancak yineleme olasılığı olmadan. Bu nasıl anlaşılmalı? İhtiyaç duyulan başka birçok şey var, ama nedense kimse uygulamaya zahmet etmedi. Aynı GetHashCode genellikle korkunçtur. Üzgünüm ama bu MQ seviyesi değil!

 
Vasili Sokolov :

Roman, bunu orada yaptığını sanmıyorum. Şimdi bazı genel sınıflar en gerekli yöntemleri bile içermiyor. Onları hiç denedin mi? Örneğin, kırmızı-siyah bir ağacın klasik bir uygulaması olan CRedBlackTree'yi ele alalım. Oldukça havalı bir algoritma, ancak yineleme olasılığı olmadan. Bu nasıl anlaşılmalı? İhtiyaç duyulan başka birçok şey var, ama nedense kimse uygulamaya zahmet etmedi. Aynı GetHashCode genellikle korkunçtur. Üzgünüm ama bu MQ seviyesi değil!

Genric kitaplığındaki tüm şablon koleksiyonları için yineleyicilere olan ihtiyacı çok iyi anlıyorum, ancak iç içe sınıflar ve arabirimlerden çoklu kalıtım oluşturma yeteneği olmadan, bunları doğru şekilde uygulamak işe yaramaz.

GetHashCode ile ilgili olarak, daha spesifik yorumlar duymak istiyorum.

 
Roman Konopelko :

...

GetHashCode ile ilgili olarak, daha spesifik yorumlar duymak istiyorum.

Sorun

Açıkçası, GetHashCode, MQL5 kullanıcı ortamında düzgün bir şekilde uygulanamaz. Bunun nedeni, tüm nesnelere erişilememesidir. Ve eğer double int, vb. Gibi ilkel üyelerde ise. uygulama iyi çalışıyor, daha sonra karmaşık nesneler (sınıflar, yapılar ve hatta numaralandırmalar) için karma ada göre hesaplanır. Açıkçası, CObject türünde veya hatta ENUM _something_there türünde bin nesnemiz varsa, o zaman hem CHashMap hem de CHashSet normal bir LinkedList'e dönüşür:

 #include <Generic\HashSet.mqh>
//+------------------------------------------------------------------+
//| Безобидное перечисление, но может быть класс или структура       |
//+------------------------------------------------------------------+
enum ENUM_NUMBER
{
   NUMBER_FIRTS,   // First
   NUMBER_SECOND, // Second
   NUMBER_THIRD   // Third
};
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart ()
{
   CHashSet<ENUM_NUMBER> set_enum;
   for ( int i = 0 ; i < 3 ; i++)
      set_enum.Add((ENUM_NUMBER)i);
   //-- Поздравляю, мы выродились в LinkedList.... 
   for ( int i = 0 ; i < 3 ; i++)
   {
       //-- Здесь дикий оверхед по производительности, который тчательно скрывается...
       string is = set_enum. Contains((ENUM_NUMBER)i) ? " присутствует " : " отсутствует " ;
       //...
   }
}

Bundan kaçınmanın bir yolu yok, çünkü kullanıcı düzeyinde sahip olduğumuz tek şey nesnenin adı:

 //+------------------------------------------------------------------+
//| Returns a hashcode for custom object.                            |
//+------------------------------------------------------------------+
template < typename T>
int GetHashCode(T value)
  {
//--- try to convert to equality comparable object  
   IEqualityComparable<T>*equtable= dynamic_cast <IEqualityComparable<T>*>(value); // <-- Здесь будет получено название класса, структуры или перечисление
   if (equtable)
     {
       //--- calculate hash by specied method   
       return equtable.HashCode();
     }
   else
     {
       //--- calculate hash from name of object
       return GetHashCode( typename (value));
     }
  }

Bu nedenle, karmaşık türlerdeki tüm nesnelerin karma değerleri birbirine eşittir ve bu arama kodu, dizinin tam bir aramasını kullanır:

 //+------------------------------------------------------------------+
//| Determines whether a set contains the specified element.         |
//+------------------------------------------------------------------+
template < typename T>
bool CHashSet::Contains(T item)
  {
//--- check buckets
   if ( ArraySize (m_buckets)!= 0 )
     {
       //--- get hash code for item      
       int hash_code=m_comparer.HashCode(item);
       //--- search item in the slots       
       for ( int i=m_buckets[hash_code% ArraySize (m_buckets)]- 1 ; i>= 0 ; i=m_slots[i].next)
         if (m_slots[i].hash_code==hash_code && m_comparer.Equals(m_slots[i].value,item))
             return ( true );
     }
   return ( false );
  }

Aslında, bu o kadar kritik ki, tüm Jenerik'i tomurcukta kullanma noktasını aşıyor. Gerçek şu ki, çoğu durumda karmaşık nesnelerin ilişkilendirilmesi veya depolanması gerekir: numaralandırmalar, yapılar veya sınıflar. Yalnızca basit tiplerin çalıştırılması gerekseydi, daha basit bir şey yapılabilirdi.


Karar

Açıkçası, MQL5, dahili bir sanal makinede çalışan, güvenli bir özel programlama dilidir. Net gibi bir şey. Bu, nesnelere gerekli erişimin hala orada olduğu, ancak daha sistematik bir düzeyde olduğu anlamına gelir. Bu nedenle, aşağıdaki prototiple bir GetHashCode sistem işlevi yazmak mümkündür :

 int GetHashCode ( void sample);

Nasıl çalışmalı? İlk olarak, omnivordur ve hızlı olmalıdır. Eşit bir dağıtım yapın. Örneğin:

 int hash = GetHashCode ( 3 );
// hash = 3

hash = GetHashCode ( 3.14159 );
// hash = 2039485

enum EType{E1, E2, E3};
hash = GetHashCode (E2);
// hash = 2

class C1{};
C1* class1 = new C1();
hash = GetHashCode (class1);
//hash = 39203847

struct S1{};
S1 struct1;
hash = GetHashCode (struct1);
//hash = 83928342

Bu fonksiyon, sistem fonksiyonları bölümünde bulunabilir. Başka çözümler göremiyorum.

ps Biraz sonra arayüzler, çoklu kalıtım ve diğer C++ mirası hakkında başka önerilerde bulunacağım.