Libreria di classi generiche - bug, descrizione, domande, caratteristiche d'uso e suggerimenti - pagina 10

Ti stai perdendo delle opportunità di trading:
- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Registrazione
Accedi
Accetti la politica del sito e le condizioni d’uso
Se non hai un account, registrati
Informazioni sulle funzioni hash
Dagli esempi precedenti, è chiaro che la funzione hash prende il grosso del carico. Una funzione hash può essere il più veloce possibile, ma molto probabilmente sarà molto incline alle collisioni. E viceversa, una funzione hash può essere resa il più possibile resistente alle collisioni, ma la sua complessità computazionale sarà inadeguata al compito da svolgere. Consideriamo due esempi estremi. Esempio 1, la funzione hash più veloce possibile:
Restituirà sempre lo stesso numero. Se il nostro dizionario memorizzerà una sola parola, allora il suo lavoro sarà sufficiente, perché questa parola si troverà all'indice 0. Se però abbiamo 10 parole, allora tutte e dieci le parole saranno all'indice 0 (non dimenticate che abbiamo un array di array).
Per il secondo esempio, passiamo a una crittografia reversibile, per esempio basata su una rete Feistel con numero di giri N. Questa non è una funzione hash, e non è affatto soggetta a collisioni. Cioè per due stringhe diverse otterremo numeri diversi garantiti. La lunghezza dei numeri ottenuti sarà uguale alla lunghezza della stringa. Quindi, se la stringa è lunga 1000 caratteri, otterremo un array uchar con dimensione 1000. Se abbiamo bisogno di memorizzare questa stringa in un dizionario di 3 elementi, non pensate che sarebbe strano calcolare un codice così complesso per trovare una parola con solo tre elementi.
Quindi, dovremmo sempre cercare una via di mezzo (come giustamente sottolineato da fxsaber): abbiamo bisogno di una funzione che calcoli rapidamente gli hash e di solito non ottiene collisioni. Stimiamo la probabilità di collisioni per la nostra precedente funzione hash:
Ci sono 26 caratteri nell'alfabeto. Supponiamo che il numero di parole che iniziano con il simbolo 'a' sia uguale al numero di parole che iniziano con qualsiasi altro simbolo. Allora se il nostro dizionario contiene 988 parole, 38 di esse iniziano con a, 38 con b, 38 con c, ... 38 alla lettera w e 38 alla lettera - z. La probabilità che si verifichi una collisione in ogni caso sarebbe 1/26 o 3,8461%. Se abbiamo 988 parole, allora otteniamo 988*3,8461 = 37,999 parole per indice.
È chiaro che più parole ci sono per una stessa lettera, più è difficile trovare una parola particolare. Nel nostro caso non dobbiamo solo calcolare l'indice della prima lettera, ma anche cercare tutte le parole che iniziano con la stessa lettera. Per evitare questo, possiamo complicare la funzione hash:
Cioè prendiamo i primi due caratteri della parola. Allora le combinazioni possibili non saranno 26, ma 26^2 = 676. Va notato che la complessità della seconda variante di calcolo della funzione hash è aumentata linearmente, più o meno della metà, mentre la sua resistenza alle collisioni è aumentata in modo non lineare, di un quadrato.
Per la seconda variante, la media sarebbe una collisione ogni 676 parole. Nel nostro caso, per 988 parole, ci sarebbero 988/676 = 1,4615 collisioni per indice. Ciò significa che ogni indice conterrà in media o una parola o 2 parole. Quindi in media non ci saranno collisioni (un confronto), o saranno molto brevi (due confronti).
Se il rapporto tra la dimensione del dizionario e i cobmins della funzione hash è vicino a 1.0000000, allora in media non sarà necessaria la forza bruta, perché ogni elemento si troverà da solo al suo indice. Se una funzione hash fornirà una gamma molto ampia di valori, il rapporto tra la dimensione del dizionario e le possibili combinazioni della funzione hash sarà sempre inferiore a 1,0. Per esempio, se una funzione hash produce 2^32 combinazioni, allora per qualsiasi dimensione N inferiore a 2^32 il rapporto N/2^32 < 1,0 sarà valido. Se N è molto piccolo, allora si normalizza semplicemente il numero ottenuto dalla funzione hash, in modo che sia sempre nel limite di N:
int index = GetHashCode(word)%ArraySize(m_array);
Se la funzione di hash genera risultati in modo uniforme, in media, otterremo un rapporto di 1.000. Cioè ci sarà solo una parola in un indice dell'array. Così possiamo dire che un dizionario è un caso speciale di un array con una chiave invece di un indice.
La dimensione dell'array può essere facilmente cambiata per corrispondere alla dimensione del dizionario.
Non prendo in considerazione le varianti infinite.
Il punto è che la dimensione del dizionario è spesso sconosciuta. Un semplice esempio, diciamo che abbiamo un Expert Advisor che fa trading. Tiene traccia delle transazioni effettuate. Una volta che un trade appare nella storia, abbiamo bisogno di collegare questo trade con il Medjik dell'EA. Per questo è logico usare il dizionario. Dove il numero di trade è usato come chiave (identificatore unico), e il numero magico dell'Expert Advisor è usato come valore. Il problema è che all'inizio dell'EA non possiamo determinare in anticipo se avremo 100 trade, 1000 o nessun trade. Qualunque sia la memoria allocata in anticipo, sarà o troppo poca o troppo grande.
Questo è il punto: la dimensione del dizionario è spesso sconosciuta. Un semplice esempio, diciamo che abbiamo un consulente che fa trading. Tiene traccia delle transazioni effettuate. Quando un trade appare nella storia, dobbiamo collegare questo trade con il Medjack dell'Expert Advisor. Per questo è logico usare il dizionario. Dove il numero di trade è usato come chiave (identificatore unico), e il numero magico dell'Expert Advisor è usato come valore. Il problema è che all'inizio dell'EA non possiamo determinare in anticipo se avremo 100 trade, 1000 o nessun trade. Non importa quanta memoria si alloca in anticipo, sarà sempre o troppo poca o troppo grande.
Beh, ovviamente, ci sono diversi compiti.
Stavo risolvendo un compito in cui dovevo creare un dizionario conveniente per una lingua umana. La dimensione del mio array non è esatta, ma che problema c'è ad adattarla al numero di parole di una lingua specifica? Può entrare facilmente lì dentro.
Per esempio una lingua russa. Supponiamo che contenga 10 000 parole base. Tutte queste parole si adattano bene alla mia matrice.
La prima dimensione - 66 lettere (piccole e grandi), la seconda dimensione - il numero di lettere nella parola (fino a 25 per esempio), la terza dimensione - corrisponde alla prima lettera e il numero di lettere - 50.
Tutta la lingua si adatta.
//----------------------------
Inizialmente, stavo risolvendo esattamente questo problema. Ora stai sostituendo il problema originale con un altro e dicendo che la soluzione non è buona.
Non importa quanta memoria si alloca in anticipo, sarà o troppo poca o troppo grande.
Giusto.
Così come è vero che non esiste una soluzione universale per tutti i compiti.
Vero.
È anche vero che non c'è una soluzione unica per tutti.
Completamente indiscutibile.
Abbassa la voce, compagno. Nessuno è immune dagli errori, nemmeno tu. Quindi siate abbastanza gentili da far notare gli errori degli altri con un tono più morbido. Lo correggerò ora.
Quello giusto rimarrà un segreto?
Non ci può essere una variante corretta di tabelle hash e hash in linea di principio - tutto dipende dagli scopi specifici e dalle condizioni di applicazione.
È come fare un panino. Forse qualcuno non mangia la maionese o il ketchup, o è un vegan....
Ma mettere il ketchup sul tavolo e metterci sopra il pane - è abbastanza chiaro che non è ....
Nozioni di base sull'argomento per i pigri:
https://www.slideshare.net/mkurnosov/6-32402313
In realtà è molto più complicato e viene discusso nella letteratura pertinente o in buoni corsi di "algoritmi e strutture dati".
In breve, la funzione di hash dovrebbe funzionare in primo luogo: rapidamente, in secondo luogo, non c'è bisogno di inventare qualcosa di molto complicato, perché l'aspetto degli stessi valori è normalmente risolto all'interno del dizionario per forza bruta diretta. L'importante è non avere collisioni troppo frequenti, tutto qui. In Generic, un insieme di funzioni nel file HashFunction.mqh è responsabile dell'hash delle funzioni dei tipi base. Per essere sicuri che tutto sia semplice lì, vediamo come è organizzato:
Itipi interi sono restituiti così come sono, o per i tipi interi corti, vengono usate operazioni di conversione bitwise non complicate. Per i tipi che non entrano in 32 cifre, si usa un o esclusivo seguito da un join sinistra-destra. Per le stringhe, un hash non complicato è anche calcolato tramite forza bruta diretta. Per i numeri reali si prende una rappresentazione in bit con l'unione e la si usa come hash.
Gli algoritmi di tipo dizionario sono convenzionalmente divisi in due parti:
Cos'è un CHashSet? È una collezione (un array per sua natura) dove ogni elemento è unico. Per esempio, una tale collezione può contenere i numeri 2,5,1,7,10,15,1024. Ma due numeri non possono essere uguali. Può anche contenere stringhe, numeri reali e persino tipi complessi personalizzati (di cui parleremo più avanti). Ma la regola è la stessa per qualsiasi tipo - non ci può essere un altro dello stesso tipo nel dizionario.
Cos'è CHashMap? È una collezione (anche un array per natura), dove ogni elemento è un tipo di chiave-valore complesso:
È strutturata quasi esattamente come CHashMap, ma permette una corrispondenza tra due insiemi tale che un elemento dell'insieme 1 corrisponde a un elemento dell'insieme 2. Questa è una cosa molto comoda, permettendo di scrivere algoritmi di interrogazione molto efficienti con un tempo medio di esecuzione di O(1).
Più avanti, guarderemo degli esempi per vedere come costruire una sorta di corrispondenza.
È interessante notare che qualsiasi dizionario chiave-valore stabilisce naturalmente una relazione non banale comeuno a molti. Per esempio, abbiamo un mucchio di ordini storici e un mucchio di trade che sono stati eseguiti sulla base di essi. Ogni trade corrisponde a un solo ordine, ma un ordine può corrispondere a più trade. Il dizionario di una tale relazione può memorizzare come chiave il numero del commercio, e come valore, il numero dell'ordine che è servito per aprirlo.