Biblioteca de clases genéricas - errores, descripción, preguntas, características de uso y sugerencias - página 23

 
//+------------------------------------------------------------------+
//|                                              CompareFunction.mqh |
//|                   Copyright 2016-2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Generic\Interfaces\IComparable.mqh>
//+------------------------------------------------------------------+
//| Compares two objects and returns a value indicating whether one  |
//| is less than, equal to, or greater than the other.               |
//+------------------------------------------------------------------+
int Compare(const bool x,const bool y)
  {
   if(x>y)
      return(1);
   else if(x<y)
      return(-1);
   else
      return(0);
  }


La función se declara globalmente. Por ello, existe un conflicto con su Comparación por parte de los usuarios.

'Compare' - function already defined and has body

Para reducir los conflictos de nomenclatura, ¿podría el autor convertir todas las funciones genéricas auxiliares globales en métodos públicos-estáticos?

 

fxsaber:

Para reducir los conflictos de nomenclatura, ¿podría el autor convertir todas las funciones genéricas auxiliares globales en métodos públicos-estáticos?

Foro sobre comercio, sistemas de comercio automatizados y pruebas de estrategias

Error del compilador: 'operator=' - la estructura tiene objetos y no se puede copiar

fxsaber 2018.09.10 11:00 2018.09.10 10:00:13 RU
Alexey Navoykov:
Esto es por el momento. Si quiere incluir la biblioteca de alguien, descubrirá que el autor escribe tan "primitivo" como usted, utilizando los mismos nombres de clases y funciones.

Los clavaré con macros.

¿Qué pasa con las macros?
 
A100:
¿Qué pasa con las macros?

No estaba hablando de mí.

 

He leído todas las páginas de la discusión, pero todavía no entiendo cómo usarlo?

¿Puede alguien darme algunos ejemplos?

 
Vladimir Pastushak:

He leído todas las páginas de la discusión, pero todavía no entiendo cómo usarlo?

¿Puede alguien darme algunos ejemplos?

Olvídalo. No se puede utilizar tal y como está ahora. En su lugar, utilice el CObject estándar + CDictionary. Para la mayoría de las tareas, es suficiente.

 
Vasiliy Sokolov:


ClaseDescripción
CHashMapUn conjunto clave-valor. Permite insertar, recuperar y buscar elementos de forma eficaz en función de su clave. La clave debe ser única. Por ejemplo, CHashMap puede encontrar instantáneamente un pedido con un número especificado, donde el número se utiliza como clave.

Pregunta sobre la recuperación de un valor por clave. En el código de la biblioteca, este método tiene el siguiente aspecto

//+------------------------------------------------------------------+
//| Find index of entry with specified key.                          |
//+------------------------------------------------------------------+
template<typename TKey,typename TValue>
int CHashMap::FindEntry(TKey key)
  {
   if(m_capacity!=NULL)
     {
      //--- get hash code from key
      int hash_code=m_comparer.HashCode(key)&0x7FFFFFFF;
      //--- search pair with specified key
      for(int i=m_buckets[hash_code%m_capacity]; i>=0; i=m_entries[i].next)
         if(m_entries[i].hash_code==hash_code && m_comparer.Equals(m_entries[i].key,key))
            return(i);
     }
   return(-1);
  }

Las herramientas de navegación de ME (ALT+G y CTRL+-) por origen se niegan a funcionar en esta biblioteca. Por lo tanto, es muy difícil seguir la lógica. En particular, aún no he averiguado el valor inicial en el bucle resaltado. Pero se entiende que si hay una velocidad, este valor debe ser mucho menor que el número de llaves.


Por favor, aclare la idea, ¿cuál es la velocidad alcanzada en esta función? La exageración está claramente presente. Pero aparentemente es corto por alguna razón.


SZ he revisado mi depurador paso a paso. Todo claro en el ejemplo TKey = int: m_bucket[Array[i]] = i. Sólo las colisiones cuando Array[i] == Array[j] (i != j) no son claras.

 
fxsaber:

La pregunta es sobre la obtención del valor por la clave. En el código de la biblioteca este método tiene el siguiente aspecto
Por favor, aclare la idea, ¿qué hace que esta función sea rápida? El rebasamiento está obviamente presente. Pero aparentemente es corto por alguna razón.

En un momento dado estuve revisando y describiendo cómo funcionaCHashMap
. Tienes que buscar las entradas, probablemente en este hilo.

 

Foro sobre trading, sistemas de trading automatizados y pruebas de estrategias de trading

Biblioteca de clases genéricas - errores, descripción, preguntas, peculiaridades de uso y sugerencias

Sergey Dzyublik, 2017.12.11 13:41

Brevemente sobre la implementación actualde CHashMap:

template<typename TKey,typename TValue>
class CHashMap: public IMap<TKey,TValue>
  {
protected:
   int               m_buckets[];                        
   Entry<TKey,TValue>m_entries[];
   int               m_count;
   int               m_free_list;
   int               m_free_count;
   IEqualityComparer<TKey>*m_comparer;
   bool              m_delete_comparer;

     .................................

.................................

}

En primer lugar, vamos a averiguar qué esEntry<TKey,TValue>.
Esencialmente es un Nodo como en CLinkedList que contiene:

- colocados en la clave y el valor del contenedor
- hash_code - un valor hash en caché para la clave
- next - "puntero" a la siguienteEntry<TKey,TValue> con el fin de resolver la colisión a través de la lista de conexión única


m_entries[] - matriz de "celdas" donde se colocan la clave y el valor añadidos, hash_code, next. El tamaño de la matriz corresponde a la Capacidad.
m_count - especifica el índice de la primera celda no utilizada en m_entries. El valor inicial es 0, creciendo hasta la Capacidad, lo siguiente es reconstruir todas las matrices con el aumento de la Capacidad y el tamaño de todas las matrices.
m_buckets[] - matriz de índices en m_entries[]. El valor por defecto es -1. El tamaño de la matriz corresponde a la capacidad.


Sin colisión, añadiendo un valor único al contenedorCHashMap:

  1. Calcular el hash por clave, obtener hash_code
  2. Poner clave, valor, código_hash en m_entradas[] por índice m_count. (next = -1 porque el ejemplo no tiene colisiones)
  3. Poner en m_buckets[] por hash_code % (ArraySize(m_buckets)) el valor del índice del elemento recién añadido en m_entries[] (esto es m_count).
  4. Aumentar m_count en +1

Sin colisiones, obtener el valor por clave en el contenedorCHashMap:

  1. Calcular el hash de la clave, obtener el código hash
  2. De m_buckets[], por hash_code % (ArraySize(m_buckets)) obtener el valor que es un índice del array m_entries[]
  3. Usando el índice obtenido m_buckets[hash_code % (ArraySize(m_buckets))], obtenemos nuestro valor del array m_entries[].

Resolución de colisiones:

Para claves diferentes, puede ocurrir que hash_code_key_1 % (ArraySize(m_buckets)) sea igual a hash_code_key_2 % (ArraySize(m_buckets)) Esto se llama colisión.
Todos los elementos con colisión añadidos a m_entries[] se enlazan a través de la lista conectada con next (ver estructuraEntry<TKey,TValue>)
Y m_buckets[] siempre apunta al índice del primer elemento de cabeza de esta lista de colisión.
No obtenemos una gran lista con colisiones, sino muchas listas pequeñas.


Colisión, obtener el valor por clave en el contenedorCHashMap:

  1. Sobre la clave calculamos el hash, obtenemos hash_code
  2. Usando el índice hash_code % (ArraySize(m_buckets)), obtén el valor del array m_entries[]
  3. Podemos ver que el valor de next no es igual a -1, lo que indica que hay una colisión
  4. Recorre la lista de una sola entrada formada por elementos de m_entries[] y compara el valor buscado y la clave guardada para comprobar la igualdad


Eliminación de un valor del contenedorCHashMap:

- Cuando se borra una celda en m_entradas[], no se realiza ningún borrado; la celda se marca como libre y hash_code = -1.
- las celdas libres forman su propia lista de celdas libres (usando el mismo siguiente de Entry<TKey,TValue>),m_free_list
-
las celdas libres se usan primero para añadir nuevos valores al contenedor.
-m_free_count se utiliza para controlar el número actual de celdas libres


Reconstruir la tabla hash (proceso de aumento de capacidad) :

- reconstruir sólo cuando la capacidad está llena al 100%.
- El siguiente tamaño de capacidad se toma de la lista:

const static int  CPrimeGenerator::s_primes[]=
  {
   3,7,11,17,23,29,37,47,59,71,89,107,131,163,197,239,293,353,431,521,631,761,919,
   1103,1327,1597,1931,2333,2801,3371,4049,4861,5839,7013,8419,10103,12143,14591,
   17519,21023,25229,30293,36353,43627,52361,62851,75431,90523,108631,130363,156437,
   187751,225307,270371,324449,389357,467237,560689,672827,807403,968897,1162687,1395263,
   1674319,2009191,2411033,2893249,3471899,4166287,4999559,5999471,7199369
  };

- Las matrices m_entries[] y m_buckets[] se incrementan.
- m_buckets[] se limpia y se rellena completamente, basándose en los datos de m_entries[] (valor hash almacenado para la clave - hash_code)


Comportamiento descrito a partir de2017.12.11
Actualmente, puede haber añadido/cambiado algunos detalles/coeficientes.

 
Sergey Dzyublik:

En algún momento he desmontado y descrito cómo funcionaCHashMap
Tienes que buscar las entradas, probablemente en este hilo.

Lo encontré en

Foro sobre comercio, sistemas de comercio automatizados y pruebas de estrategias

Biblioteca de clases genéricas - errores, descripciones, preguntas, peculiaridades de uso y sugerencias

Sergey Dzyublik, 2017.12.07 14:21


En este ejemplo, el hash es la fecha de nacimiento del estudiante.
Tenemos un armario con 365 estantes que contienen las agendas de los alumnos.
Colocamos cada agenda en la estantería correspondiente al cumpleaños del alumno.

Necesitamos el diario del alumno Petrov y sabemos cuándo nació.
Ahora por la fecha de nacimiento en O(1) podemos encontrar fácilmente el diario de Petrov, así como el de cualquier otro estudiante.
La excepción es cuando dos estudiantes cumplen años el mismo día, lo que se denomina colisión.

Resolver una colisión es utilizar la información extra para averiguar cuál de dos o más diarios necesitamos.
La resolución de colisiones a través de una lista consiste simplemente en recorrer todas las entradas de la colisión una por una para encontrar una coincidencia. Arranca cada diario y mira de quién es.
Un subtítulo está organizando una tabla hash de los elementos implicados en la colisión, pero con un criterio diferente. Por ejemplo, por la hora de nacimiento del alumno.


Si te interesa el tema - te aconsejo un curso de MailRu en youtube sobre algoritmos y estructuras de datos.

Foro sobre trading, sistemas de trading automatizados y pruebas de estrategias de trading

Biblioteca de clases genéricas - errores, descripción, preguntas, peculiaridades de uso y sugerencias

Sergey Dzyublik, 2017.12.08 14:40

Los fundamentos de este tema son para los perezosos:
https://www.slideshare.net/mkurnosov/6-32402313

En realidad es mucho más complicado y se discute en la literatura pertinente o en buenos cursos de "algoritmos y estructuras de datos".


Gracias, la depuración ayudó. Hay pequeñas listas de colisiones. He revisado el hilo y me he horrorizado. Resulta que estaba en el tema...

 
Sergey Dzyublik:
Comportamiento descrito a partir de2017.12.11

A partir de ahora, puede haber añadido/cambiado algunos detalles/coeficientes.

¡Muchas gracias! Ayudó mucho su descripción.

Razón de la queja: