diff --git a/TP2_hachage/.Rhistory b/TP2_hachage/.Rhistory new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/TP2_hachage/Histogramme 7B.png b/TP2_hachage/Histogramme 7B.png new file mode 100644 index 0000000000000000000000000000000000000000..06e6e7ef8664933e63df15d9f51bc40addc0e260 Binary files /dev/null and b/TP2_hachage/Histogramme 7B.png differ diff --git a/TP2_hachage/Histogramme 7C.png b/TP2_hachage/Histogramme 7C.png new file mode 100644 index 0000000000000000000000000000000000000000..d8f9312004ba50e92aeeb33d609eab395a6fec88 Binary files /dev/null and b/TP2_hachage/Histogramme 7C.png differ diff --git a/TP2_hachage/Readme.md b/TP2_hachage/Readme.md index c66afd82f9b56ffd28631c8efd87208dd6e36278..774dcd4ac8c16bdc46f5030d8a7836fc53b68362 100644 --- a/TP2_hachage/Readme.md +++ b/TP2_hachage/Readme.md @@ -55,6 +55,10 @@ Initialiser dans la fonction `experiment_load_factor` des listes `insertion_time 6. A quoi sert la fonction `experiment_longest` ? +La fonction experiment_longest parcourt, par une itération, les nombres de 0 à 999 et stocke chaque valeur dans un dictionnaire +avec une clé correspondant au nombre sous forme de chaîne de caractères. +À chaque insertion d'un élément dans le dictionnaire, le temps d'insertion est mesuré et ajouté dans un histogramme. + ## Visualisation des résultats 7. Créez quatre graphiques au format png ou pdf : @@ -65,3 +69,27 @@ Initialiser dans la fonction `experiment_load_factor` des listes `insertion_time - Un histogramme des fréquences des temps d'insertions discrétisés (code fourni, remplacer la liste vide par la bonne entrée) 10. Commentez vos résultats. + +7A : Ce graphique représente le temps d'insertion des éléments dans le dictionnaire en fonction du facteur de charge. +Nous avons calculé la moyenne du temps d'insertion pour chaque facteur de charge et l'avons représentée sur le graphique. +Nous nous attendions à ce que le temps d'insertion soit plus faible pour les petits facteurs de charge, mais nous avons observé l'inverse, sans pouvoir l'expliquer. +Nous avons cherché un éventuel biais en utilisant la médiane au lieu de la moyenne, mais les résultats sont restés identiques. +Nous pouvons cependant noter qu'un dictionnaire possède une taille initiale définie. +Lorsqu'un élément est ajouté, il faut parcourir le dictionnaire et trouver un emplacement libre, ce qui explique le temps d'insertion observé, qui est censé être plus grand avec le facteur de charge. + +7B : Ce graphique illustre le nombre de réallocations de mémoire en fonction du facteur de charge. +On remarque que les petits facteurs de charge nécessitent peu d'espace, et donc peu de réallocations. +En revanche, pour les plus grands facteurs de charge, la mémoire requise augmente, entraînant davantage de réallocations. +On observe également des plateaux dans le graphique. En effet, la taille du dictionnaire double à chaque réallocation, +laissant un espace libre suffisant pour insérer de nouveaux éléments sans nécessiter immédiatement une nouvelle réallocation. +Ainsi, on insère jusqu'à saturation, puis une nouvelle réallocation est déclenchée. + +7C : Ce graphique représente la taille de la mémoire occupée en fonction du nombre d'éléments insérés. +On observe que pour les petites valeurs, le nombre de réallocations de mémoire est faible. +À mesure que le facteur de charge augmente, la taille des réallocations croît également. +Cependant, on note des écarts importants à certains moments. Cela s'explique par le mécanisme des réallocations : la taille du dictionnaire double lorsque l'espace est insuffisant. +Par conséquent, pour certaines valeurs du facteur de charge, il reste suffisamment d’espace pour insérer plusieurs éléments sans nécessiter de nouvelle réallocation. + +Histogramme.png : Ce dernier graphique montre la distribution des temps d'insertion sous forme d'histogramme. +On remarque que la plupart des valeurs sont insérées très rapidement dans le dictionnaire, tandis qu'un faible nombre d'insertions prennent plus de temps. +Cela s’explique par le fait que le dictionnaire doit parfois parcourir son espace de stockage pour trouver une position libre avant d'insérer la nouvelle clé et sa valeur. diff --git a/TP2_hachage/histogramme 7A.png b/TP2_hachage/histogramme 7A.png new file mode 100644 index 0000000000000000000000000000000000000000..d999847cc5c96e3ee6f76c7a32e834dcf6e26361 Binary files /dev/null and b/TP2_hachage/histogramme 7A.png differ diff --git a/TP2_hachage/histogramme.png b/TP2_hachage/histogramme.png new file mode 100644 index 0000000000000000000000000000000000000000..a55457828d1c298051b9c3e7e7f181fc44a65092 Binary files /dev/null and b/TP2_hachage/histogramme.png differ diff --git a/TP2_hachage/mphf.png b/TP2_hachage/mphf.png new file mode 100644 index 0000000000000000000000000000000000000000..105fa489e095323b30b7d37edf3d473793c223af Binary files /dev/null and b/TP2_hachage/mphf.png differ diff --git a/TP2_hachage/tp_2_miso_dict.py b/TP2_hachage/tp_2_miso_dict.py index 4d8763c9079371ce4b7d6550cd44bca99333d688..e2faa728c681964caa52432747bf3e19e4a3d9c8 100644 --- a/TP2_hachage/tp_2_miso_dict.py +++ b/TP2_hachage/tp_2_miso_dict.py @@ -11,8 +11,8 @@ import math def experiment_load_factor(load_factors): # = load_factor = liste de facteur de charge """ - Étude du facteur de charge - """ + Étude du facteur de charge + """ insertion_times = [] num_resizes = [] sizes = [] @@ -44,11 +44,11 @@ def experiment_load_factor(load_factors): # = load_factor = liste de facteur de def experiment_longest(): """ - TODO: cette focntion apour objectif de créer un dictionnaire, à partir d'une valleur de départ, - pour chaque iteration, elle associé une clé qui est la chaine de caractère de la valeur. A chaque insertion, le temps - est ajouté est mésuré et est stocké dans une variable. - A la fin un histogramme est créer et stocké dans un histogramme. - """ + TODO: cette focntion apour objectif de créer un dictionnaire, à partir d'une valleur de départ, + pour chaque iteration, elle associé une clé qui est la chaine de caractère de la valeur. A chaque insertion, le temps + est ajouté est mésuré et est stocké dans une variable. + A la fin un histogramme est créer et stocké dans un histogramme. + """ d = {} insertion_times = [] @@ -65,8 +65,8 @@ def experiment_longest(): def visualisation(load_factors, insertion_times, num_resizes, sizes, frequencies): """ - Visualisation des résultats - """ + Visualisation des résultats + """ # Temps d'insertion en fonction du facteur de charge plt.figure(figsize=(10, 6)) plt.bar(load_factors, insertion_times, width=0.01, label="insertion_time", color='blue') diff --git a/TP2_hachage/tp_2_miso_mphf.py b/TP2_hachage/tp_2_miso_mphf.py index 99b2ab589f44750a1ad7ef01eb52663eb8577591..6bbf6a880fd480a255271be44116641431866b0b 100644 --- a/TP2_hachage/tp_2_miso_mphf.py +++ b/TP2_hachage/tp_2_miso_mphf.py @@ -8,8 +8,7 @@ import random ###### PARTIE 1 ###### def construction_mphf(set_kmer, n, gamma=2, nb_niveaux=3): - - """ + """ Construit une fonction de hachage minimale parfaite (MPHF) pour un ensemble de k-mers. Parameters: @@ -32,52 +31,52 @@ def construction_mphf(set_kmer, n, gamma=2, nb_niveaux=3): >>> len(mphf) == n True """ - # Initialisation - set_kmer_courant = set_kmer.copy() - tableaux = [] - collision = set() - for _ in range(nb_niveaux): - if len(set_kmer_courant) > 0: - l = len(set_kmer_courant) - tableau_principal = [-1] * (gamma * l) - for kmer in set_kmer_courant: - index = abs(hash(kmer)) % (gamma*l) - if tableau_principal[index]!= -1: - collision.add(kmer) - else : - tableau_principal[index] = index - - tableaux.append(tableau_principal) # expliquer - set_kmer_courant = collision.copy() # expliquer - collision = set() # expliquer je pense que c'est pour éliminer les collisoin de même valeurs - - # Construction de la MPHF - mphf = [] - grand_tableau = [] - for tableau in tableaux: - grand_tableau.extend(tableau) # expliquer elle va ajouté toute les valeurs du tableau - - rangs = [] - max_rang = 0 - i = 0 - for kmer in set_kmer: - hacher = abs(hash(kmer)) - if hacher in grand_tableau : - index = grand_tableau.index(hacher) - rangs.append(grand_tableau[:index].count(hacher)) - mphf.append([hacher, rangs[-1]]) - max_rang = max(rangs) - - for kmer in set_kmer_courant: #gestion des collisions: expliquer les 3 lignes du dessous - max_rang += 1 - h = abs(hash(kmer)) - mphf.append([h, max_rang]) - - return mphf + # Initialisation + set_kmer_courant = set_kmer.copy() + tableaux = [] + collision = set() + for _ in range(nb_niveaux): + if len(set_kmer_courant) > 0: + l = len(set_kmer_courant) + tableau_principal = [-1] * (gamma * l) + for kmer in set_kmer_courant: + index = abs(hash(kmer)) % (gamma * l) + if tableau_principal[index] != -1: + collision.add(kmer) + else: + tableau_principal[index] = index + + tableaux.append(tableau_principal) # expliquer + set_kmer_courant = collision.copy() # expliquer + collision = set() # expliquer je pense que c'est pour éliminer les collisoin de même valeurs + + # Construction de la MPHF + mphf = [] + grand_tableau = [] + for tableau in tableaux: + grand_tableau.extend(tableau) # expliquer elle va ajouté toute les valeurs du tableau + + rangs = [] + max_rang = 0 + i = 0 + for kmer in set_kmer: + hacher = abs(hash(kmer)) + if hacher in grand_tableau: + index = grand_tableau.index(hacher) + rangs.append(grand_tableau[:index].count(hacher)) + mphf.append([hacher, rangs[-1]]) + max_rang = max(rangs) + + for kmer in set_kmer_courant: # gestion des collisions: expliquer les 3 lignes du dessous + max_rang += 1 + h = abs(hash(kmer)) + mphf.append([h, max_rang]) + + return mphf def get_hash_mphf(mphf, kmer): - """ + """ Calcule le hash d'un k-mer à l'aide d'une fonction de hachage minimale parfaite (MPHF). Parameters: @@ -96,13 +95,14 @@ def get_hash_mphf(mphf, kmer): >>> 0 <= hash_mphf < n True """ - hacher = abs(hash(kmer)) - for h, rang in mphf : - if h == hacher : - return rang + hacher = abs(hash(kmer)) + for h, rang in mphf: + if h == hacher: + return rang + def create_hash_table(set_kmer, n): - """ + """ Crée une table de hachage à partir d'un ensemble de k-mers et d'une mphf Parameters: @@ -122,50 +122,51 @@ def create_hash_table(set_kmer, n): >>> all(kmer in tableau for kmer in set_kmer) True """ - mphf = construction_mphf(set_kmer, n) - tableau = n*[None] - for i in set_kmer: - hacher = get_hash_mphf(mphf, i) - if hacher : - tableau[hacher] = i - return tableau, mphf - + mphf = construction_mphf(set_kmer, n) + tableau = n * [None] + for i in set_kmer: + hacher = get_hash_mphf(mphf, i) + if hacher: + tableau[hacher] = i + return tableau, mphf def generer_kmers(n, k): - ''' + ''' genere un set de n k-mers ''' - kmers = set() - while len(kmers) < n: - kmer = ''.join(random.choice('ATCG') for _ in range(k)) - kmers.add(kmer) - return kmers + kmers = set() + while len(kmers) < n: + kmer = ''.join(random.choice('ATCG') for _ in range(k)) + kmers.add(kmer) + return kmers def compare_taille(n_max, fichier_sortie): - n_values = [] - table_size = [] - dict_size = [] - k = 21 - - for n in range(100, n_max, 1000): - set_kmer = generer_kmers(n, k) - tableau, mphf = create_hash_table(set_kmer,n) - - n_values.append(n) - table_size.append(sys.getsizeof(tableau)+sys.getsizeof(mphf)) # pourquoi ici on ne mesure pas juste la taille en mémoire du tableau ? - dict_size.append(sys.getsizeof(set_kmer)) - - plt.plot(n_values, table_size, label='Table avec MPHF') - plt.plot(n_values, dict_size, label='Dict') - plt.xlabel('n') - plt.xticks(n_values, [str(x) for x in n_values], rotation=45) - plt.ylabel('Taille (octets)') - plt.title('Évolution de la taille de la table de hachage avec MPHF et du dict') - plt.legend() - plt.savefig(fichier_sortie) - plt.close() + n_values = [] + table_size = [] + dict_size = [] + k = 21 + + for n in range(100, n_max, 1000): + set_kmer = generer_kmers(n, k) + tableau, mphf = create_hash_table(set_kmer, n) + + n_values.append(n) + table_size.append(sys.getsizeof(tableau) + sys.getsizeof( + mphf)) # pourquoi ici on ne mesure pas juste la taille en mémoire du tableau ? + dict_size.append(sys.getsizeof(set_kmer)) + + plt.plot(n_values, table_size, label='Table avec MPHF') + plt.plot(n_values, dict_size, label='Dict') + plt.xlabel('n') + plt.xticks(n_values, [str(x) for x in n_values], rotation=45) + plt.ylabel('Taille (octets)') + plt.title('Évolution de la taille de la table de hachage avec MPHF et du dict') + plt.legend() + plt.savefig(fichier_sortie) + plt.close() + # dé-commenter quand vous êtes prêts, expliquer les résultats -compare_taille(10000,"mphf.png") +compare_taille(10000, "mphf.png")