Skip to content
Snippets Groups Projects
Commit cc6b19b5 authored by Samuel Nguyen's avatar Samuel Nguyen
Browse files

Partie 2 finie. A retravailler

parent 7881f86a
No related branches found
No related tags found
No related merge requests found
tp_2_miso_dict.py
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
<component name="Black">
<option name="sdkName" value="Python 3.12" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12" project-jdk-type="Python SDK" />
</project>
\ No newline at end of file
......@@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="jdk" jdkName="Python 3.12" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
......
......@@ -2,6 +2,7 @@ import matplotlib.pyplot as plt
import numpy as np
import time
import sys
import statistics #pour faire la moyenne des temps d'insertion pour chaque facteur de charge
......@@ -21,13 +22,38 @@ def experiment_load_factor(load_factors : list):
Les nombres de réallocations de mémoire
Les tailles de mémoire occupée par le dictionnaire pour chaque facteur de charge
"""
# Initialisation
insertion_times = []
num_resizes = []
sizes = []
for factor in load_factors :
dictio = {}
# num_elements = .......... QUESTION 2 PARTIE 2
return [],[],[]
num_resize=0
last_size = sys.getsizeof(dictio)
num_elements = int(factor*100)
tempsecoules = []
for i in range(num_elements) :
cle = 'cle'+str(i)
start_time = time.time()
dictio[cle] = i
end_time = time.time()
tempsecoule = end_time - start_time
tempsecoules.append(tempsecoule)
current_size = sys.getsizeof(dictio)
if current_size > last_size :
num_resize += 1
last_size = current_size
size = sys.getsizeof(dictio)
insertion_time = statistics.mean(tempsecoules)
insertion_times.append(insertion_time)
num_resizes.append(num_resize)
sizes.append(size)
return insertion_times, num_resizes, sizes
def experiment_longest():
"""
......@@ -50,24 +76,42 @@ def visualisation(load_factors, insertion_times, num_resizes, sizes, frequencies
"""
Visualisation des résultats
"""
num_elements = []
for factor in load_factors :
num_elements.append(factor*100)
# Temps d'insertion en fonction du facteur de charge
plt.plot(load_factors, insertion_times)
plt.xlabel('Facteur de charge')
plt.xticks(load_factors, [str(x) for x in load_factors], rotation=45)
plt.ylabel("Temps d'insertion (secondes)")
plt.title("Temps d'insertion en fonction du facteur de charge")
plt.savefig("temps_d_insertion.png")
# Nombre de réallocations de mémoire en fonction du facteur de charge
plt.plot(load_factors, num_resizes)
plt.xlabel('Facteur de charge')
plt.xticks(load_factors, [str(x) for x in load_factors], rotation=45)
plt.ylabel("Nombre de réallocations de mémoire")
plt.title("Nombre de réallocations de mémoire en fonction du facteur de charge")
plt.savefig("nombre_reallocations.png")
# Taille de mémoire occupée en fonction du nombre d'éléments
plt.plot(sizes, num_elements)
plt.xlabel("Nombre d'éléments")
plt.xticks(num_elements, [str(x) for x in num_elements], rotation=45)
plt.ylabel("Taille de mémoire occupée (octets)")
plt.title("Taille de mémoire occupée en fonction du nombre d'éléments")
plt.savefig("taille_memoire.png")
# Deuxième étude
f = list()
f = list(frequencies)
plt.figure(figsize=(10, 6))
plt.bar(range(len(f)), f)
plt.xlabel('Temps d\'insertion (s)')
plt.ylabel('Fréquence')
plt.title('Histogramme des fréquences des temps d\'insertions')
plt.yscale('log')
xticks = np.logspace(-6, 1, 3)
xtick_labels = [f'{x:.1e}' for x in xticks]
plt.xticks(xticks, xtick_labels)
plt.savefig('histogramme.png')
xticks = np.logspace(-6, 1, 3)
xtick_labels = [f'{x:.1e}' for x in xticks]
plt.xticks(xticks, xtick_labels)
plt.savefig('deuxieme_etude.png')
load_factors = [0.01, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
insertion_times, num_resizes, sizes = experiment_load_factor(load_factors)
......
......@@ -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,76 +31,76 @@ 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)
if l==0 :
break #Evite une division par 0
tableau_principal = [-1] * (gamma * l)
for kmer in set_kmer_courant:
# hacher le k-mer (attention, hash() peut rendre des entiers signés, nous voulons des entiers positifs)
hachage = abs(hash(kmer))
# récupérer l'adresse
# si le tableau principal est déjà rempli à l'adresse:
# mettre le kmer dans collision()
# sinon, écrire le hash à l'adresse dans le tableau principal
adresse = hachage % (gamma * l)
if tableau_principal[adresse] != -1:
collision.add(kmer)
else:
tableau_principal[adresse] = hachage
tableaux.append(tableau_principal) # expliquer
# Chaque niveau de hachage produit un nouveau tableau de hachage.
# Donc on enregistre le tableau dans tableaux pour garder une trace des niveaux.
set_kmer_courant = collision.copy() # expliquer
# On hache à nouveau les k-mers qui ont causé des collisions.
collision = set() # expliquer
# On réinitialise collision pour le prochain niveau de hachage, pour pas que les valeurs dont on n'a plus besoin s'accumulent.
if not set_kmer_courant: # Arrête la boucle si plus de collisions
break
# Construction de la MPHF
mphf = []
grand_tableau = []
for tableau in tableaux:
grand_tableau.extend(tableau) # expliquer
# Après avoir fait tous les hachages, on regroupe les tableaux finaux de hachage dans un seul tableau.
grand_tableau_dict = {val: i for i, val in enumerate(grand_tableau)}
max_rang = 0
i = 0
for kmer in set_kmer:
# hacher le kmer
hache = abs(hash(kmer))
# si le hash est dans le grand_tableau
# récupérer son index
# récupérer son rang (utiliser la fonction count())
# ajouter à la mphf [h, rang]
# mettre à jour max_rang
if hache in grand_tableau_dict :
index = grand_tableau_dict[hache]
rang = grand_tableau[:index].count(hache)
mphf.append([hache, rang])
max_rang = max(max_rang, rang)
for kmer in set_kmer_courant: #gestion des collisions: expliquer les 3 lignes du dessous
max_rang += 1 # On attribue un nouveau rang unique à chaque élément en collision.
h = abs(hash(kmer)) # Recalcul du hash, on veut uniquement des entiers positifs.
mphf.append([h, max_rang]) # On ajoute le k-mer à mphf
# On fait ces étapes car les collisions n'ont pas pu être placées avant,
# on leur attribue un rang plus grand pour ne pas perturber l'ordre précédent
if not mphf:
print("⚠️ Attention : MPHF vide, vérifiez les données en entrée.")
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)
if l == 0:
break # Evite une division par 0
tableau_principal = [-1] * (gamma * l)
for kmer in set_kmer_courant:
# hacher le k-mer (attention, hash() peut rendre des entiers signés, nous voulons des entiers positifs)
hachage = abs(hash(kmer))
# récupérer l'adresse
# si le tableau principal est déjà rempli à l'adresse:
# mettre le kmer dans collision()
# sinon, écrire le hash à l'adresse dans le tableau principal
adresse = hachage % (gamma * l)
if tableau_principal[adresse] != -1:
collision.add(kmer)
else:
tableau_principal[adresse] = hachage
tableaux.append(tableau_principal) # expliquer
# Chaque niveau de hachage produit un nouveau tableau de hachage.
# Donc on enregistre le tableau dans tableaux pour garder une trace des niveaux.
set_kmer_courant = collision.copy() # expliquer
# On hache à nouveau les k-mers qui ont causé des collisions.
collision = set() # expliquer
# On réinitialise collision pour le prochain niveau de hachage, pour pas que les valeurs dont on n'a plus besoin s'accumulent.
if not set_kmer_courant: # Arrête la boucle si plus de collisions
break
# Construction de la MPHF
mphf = []
grand_tableau = []
for tableau in tableaux:
grand_tableau.extend(tableau) # expliquer
# Après avoir fait tous les hachages, on regroupe les tableaux finaux de hachage dans un seul tableau.
grand_tableau_dict = {val: i for i, val in enumerate(grand_tableau)}
max_rang = 0
i = 0
for kmer in set_kmer:
# hacher le kmer
hache = abs(hash(kmer))
# si le hash est dans le grand_tableau
# récupérer son index
# récupérer son rang (utiliser la fonction count())
# ajouter à la mphf [h, rang]
# mettre à jour max_rang
if hache in grand_tableau_dict:
index = grand_tableau_dict[hache]
rang = grand_tableau[:index].count(hache)
mphf.append([hache, rang])
max_rang = max(max_rang, rang)
for kmer in set_kmer_courant: # gestion des collisions: expliquer les 3 lignes du dessous
max_rang += 1 # On attribue un nouveau rang unique à chaque élément en collision.
h = abs(hash(kmer)) # Recalcul du hash, on veut uniquement des entiers positifs.
mphf.append([h, max_rang]) # On ajoute le k-mer à mphf
# On fait ces étapes car les collisions n'ont pas pu être placées avant,
# on leur attribue un rang plus grand pour ne pas perturber l'ordre précédent
if not mphf:
print("Attention : MPHF vide, vérifiez les données en entrée.")
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:
......@@ -120,15 +119,15 @@ def get_hash_mphf(mphf, kmer):
>>> 0 <= hash_mphf < n
True
"""
hache = abs(hash(kmer))
for h, rang in mphf :
if h == hache :
return rang
return -1
hache = abs(hash(kmer))
for h, rang in mphf:
if h == hache:
return rang
return -1
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:
......@@ -148,59 +147,61 @@ def create_hash_table(set_kmer, n):
>>> all(kmer in tableau for kmer in set_kmer)
True
"""
mphf = construction_mphf(set_kmer, n) # créer la mphf pour les kmers
# initialiser un tableau de taille n (une liste)
tableau = [None]*n
# écrire les kmers aux adresses du tableau données par la mphf
for kmer in set_kmer :
adresse = get_hash_mphf(mphf, kmer)
if adresse == -1:
print(f"Erreur : Impossible de trouver l'adresse pour {kmer} (MPHF renvoie -1)")
continue
if 0 <= adresse < n:
tableau[adresse]=kmer
return tableau, mphf # retourner le tableau et la mphf
mphf = construction_mphf(set_kmer, n) # créer la mphf pour les kmers
# initialiser un tableau de taille n (une liste)
tableau = [None] * n
# écrire les kmers aux adresses du tableau données par la mphf
for kmer in set_kmer:
adresse = get_hash_mphf(mphf, kmer)
if adresse == -1:
print(f"Erreur : Impossible de trouver l'adresse pour {kmer} (MPHF renvoie -1)")
continue
if 0 <= adresse < n:
tableau[adresse] = kmer
return tableau, mphf # retourner le tableau et la 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 ?
#Car sys.getsizeof(tableau) ne mesure que la liste elle-même, pas les objets stockés dedans.
# On veut mesurer la mémoire totale du système de hachage, pas seulement la liste.
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 ?
# Car sys.getsizeof(tableau) ne mesure que la liste elle-même, pas les objets stockés dedans.
# On veut mesurer la mémoire totale du système de hachage, pas seulement la liste.
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")
# Sur le graphe obtenu, on observe que la taille de la table de hachage du dictionnaire augmente par paliers,
# alors que celle de la MPHF augmente de manière linéaire, et beaucoup plus progressivement.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment