j'essayait de programmer un générateur de mots à sonorité françaises tel que l'as fait ScienceEtonnante dans sa vidéo. Après l'avoir terminé je l'ai améliorer en rajoutant la longueur des chaines de Markov en paramètre variable. Mais à partir des chaines de longueur 4 et plus le dictionnaire devient trop grand. J'ai chercher sur internet mais je n'ai trouvé aucune information aboutissante. J'aimerais donc savoir si il existe une bibliothèque ou une classe qui permet de contourner cette limitation.
Voici mon code:
Analyse.py:
# -*- coding: utf-8 -*-
from string import whitespace
def find_char(file):
# Liste de tout les caractères présent dans le fichier
chars = set()
with open(file, "r", encoding="utf-8") as f:
for char in f.read():
if not (char in whitespace or char in chars):
chars.add(char)
chars.add(" ")
c = list(chars)
c.sort()
return c
def init_dic(car, degree):
# Retourne la iste de tout les clés du dictionnaire
loop_end = False
first_run = True
foot_counter = []
liste_keys = []
for x in range(degree):
foot_counter.append(0)
while True:
for x in range(degree - 1, -1, -1):
foot_counter[x] += 1
if foot_counter[x] > len(car) - 1:
if x == 0:
loop_end = True
break
foot_counter[x] = 0
else:
break
if loop_end:
break
if foot_counter[-1] == 1 and first_run:
foot_counter[-1] = 0
first_run = False
liste_keys.append("".join([car[x] for x in foot_counter]))
return liste_keys
def correct_dic(liste_keys, file):
# Remplis le dictionnaire à partir des mots
degree = len(liste_keys[0])
dic = {x: dict() for x in liste_keys}
with open(file, "r", encoding="utf-8") as f:
f2 = " " * degree + (" " * degree).join(f.read().split("\n"))
for num in range(len(f2)):
f3 = f2[num : num + degree]
try:
dic[f3][f2[num + degree]] += 1
except IndexError:
pass
except KeyError:
try:
dic[f3][f2[num + degree]] = 1
except KeyError:
pass
except IndexError:
pass
adic = {x: y for x, y in dic.items()}
for x in adic:
if adic[x] == {}:
del dic[x]
return dic
def compile_dic(dic):
# Transforme les entrés du dictionnaire tel que "ab" : {"c": 6, "d": 3, "f": 8} en "ab" : {"c": 6, "d": 9, "f": 17}
# Pour faciliter la génération aléatoire
dico = {}
for x in dic:
dico[x] = {}
sum = 0
for y in dic[x]:
sum += dic[x][y]
dico[x][y] = sum
return dico
Generateur.py:
from random import randint
from Analyse import *
# Trouve le plus grand nombre inférieure dans à x dans une séquence
hightsmall = lambda seq, x: min([(i - x, i) for i in seq if x <= i] or [(0, None)])[1]
# Trouve la première clé dans un dictionnaire à partir de sa valeur
key = lambda v, liste: str([k for (k, val) in liste.items() if val == v][0])
fr = "francais.txt"
degree = int(
input(
"""Choisissez la précision:
2 Prend 7 seconde
3 Prend 9 seconde
4 et plus pas operationnel
>>> """
)
)
print("Analyse ...")
proba = compile_dic(correct_dic(init_dic(find_char(fr), degree), fr))
def gen_word(proba):
# Parti compliqué qu même moi ais du mal à comprendre
i = 0
word = [""]
while word[-1] != " ":
if i == 0:
rand = randint(0, max(proba[" " * degree].values()))
word[0] = key(
hightsmall(proba[" " * degree].values(), rand), proba[" " * degree]
)
elif i < degree:
rand = randint(
0,
max(
proba[
(" " * (degree - i) + "{}").format("".join(word[:i]))
].values()
),
)
lettre = key(
hightsmall(
proba[
(" " * (degree - i) + "{}").format("".join(word[:i]))
].values(),
rand,
),
proba[(" " * (degree - i) + "{}").format("".join(word[:i]))],
)
word.append(lettre)
else:
rand = randint(
0, max(proba[("{}").format("".join(word[-degree:]))].values())
)
lettre = key(
hightsmall(
proba[("{}").format("".join(word[-degree:]))].values(), rand
),
proba[("{}").format("".join(word[-degree:]))],
)
word.append(lettre)
i += 1
del rand
return "".join(word)
nb_mot = int(input("Combien de mots voulez vous generer: "))
for x in range(nb_mot):
print(gen_word(proba))
Si vous avez des remarques ou des commentaires sur mon code n'hésitez pas je suis preneur
Non il créer d'abord un tableau à partir de la liste des mots français en comptant par exemple qu'il y a tel pourcent de chance que tel lettre débute un mot puis tel pourcent pour cela qui suit et ainsi de suite. La précision vient du nombre de caractères pris en compte pour générer aléatoirement la lettre suivante. Par exemple avec deux degrés de précision, pour générer une lettre il va regarder les deux précédente puis chercher dans le tableau les probabilités des lettre suivantes pour générer la lettre. J'espère avoir été clair 😀
Je ne connais pas la grosseur du fichier que tu importes (en passant tu donnes le lien html, il y a un problème avec l'éditeur du forum?)
J'ai essayé d'imaginer toutes sortes de trucs. Comme en Python, on ne peut pas faire d'accès direct sur disque comme en C, tu n'a pas beaucoup de choix.
J'ai pensé à des listes de listes ou des dictionnaires avec sous-dictionnaires, mais l'information de contrôle augmente encore plus la mémoire demandée.
Je te suggère de télécharger le fichier une fois pour toutes et le garder sur ton PC,
Ensuite, tu peux créer un dictionnaire avec des séquences de 1, 2, 3 (ou plus) caractères, tels que 'a', 'et', 'car', 'cons', etc.
Pour chaque séquence, tu accumules la fréquence, Je suppose que tu sais comment faire:
dico = dict()
Si sequence est une séquence de caractères, tu peux faire:
if sequence not in dico.keys():
dico[sequence] = 0
dico[sequence] += 1
Si on a une fonction pseudo-aléatoire à discribution uniforme et si on veut choisir une lettre en fonction de sa fréquence probable,
On peut faire comme suit:
Tu te crée une liste des fréquences pour une lettre. Tu auras donc une liste de 26 entrées (je suppose que tu fais abstraction des majuscules).
Donc freq[0] est la fréquence pour le 'a'.
On calcule l'indice comme suit:
i = ord(lettre) - ord('a')
freq[i] = dico[lettre] # ça se fait en une ligne si tu remplaces i par sa valeur
Une fois cela fait, tu fais le calcul suivant: tu remplaces la valeur par la somme courante plus cette valeur:
sum = 0
for i in range(26):
freq[i] += sum
sum = freq[i]
La variable sum donnera la fréquence totale (toutes les lettres).
Tu calcules une valeur avec random.randrange(sum)
Si la valeur est dans l'intervalle d'une lettre, tu choisis la lettre, ça devrait donner quelque chose du genre:
lettre = random.randrange(1,sum+1)
prec = 0
for i in range(26):
if prec < lettre and lettre <= freq[i]:
lettre = chr(ord('a') + i)
break
prec = freq[i]
Tu obtiendra une lettre en proportion de la fréquence de cette lettre.
Il y a des façon plus rapides, mais qui demanderont beaucoup plus de mémoire, et tu sembles en manquer.
Le Tout est souvent plus grand que la somme de ses parties.
Bonjour j'ai modifié le lien de mon dernier message, le lien devrait fonctionner.
Je n'ai pas bien compris dans quel but tu me propose toutes ces modification, serait-ce dans le but d'alléger le dictionnaire? Si c'est le cas je ne pense pas que ce soit très optimal ou alors il faudrait en faire une class. Mon code fonctionne très bien (tu peux l'essayer si tu veux) avec des degrées inférieure à 4.
Aussi le fichier est déjà enregistré.
Pourrais tu juste édité ton message pour mettre ton code entre balise pour que ça soit plus lisible ?
Pour les balises, je ne peux pas, ma synthèse vocale ne me le permet pas (voir mon profil).
Il semble que tu n'as pas besoin de conseil pour écrire ou optimiser ton code.
> # Parti compliqué qu même moi ais du mal à comprendre
La question telle que je la conçois est "peut-on faire entrer 300 Go dans 250 Go ?"
Pour information, il y a 321272407 combinaisons de 1 à 6 lettres.
Mais il n'y en a pas autant dans la langue française.
Et quand on pense que les chaînes ne représentent qu'une fraction de l'information pour une liste ou un dictionnaire, ça peut monter assez haut.
Il n'y a pas de bibliothèque magique.
Pour ce qui est des classes, je viens de penser que les array prennent un peu moins de place que les listes ou les dictionnaires, mais sont plus difficiles à manipuler.
Ils sont moins efficaces en temps d'exécution.
Et il faut ajouter les informations de fréquence et probabilité.
Si tu peux éliminer toute structure dont tu n'as plus besoin (liste, dictionnaire, fichier ouvert, etc.) cela te donnera peut-être un peu de place.
Autrement, je ne vois rien qui peut réduire la mémoire demandée de façon significative.
edit:
J'ai un dictionnaire d'environ 325000 mots pris sur OC (jeu du pendu)
Si j'ai un peu de temps, je vais essayer ton code.
- Edité par PierrotLeFou 8 juin 2020 à 7:53:34
Le Tout est souvent plus grand que la somme de ses parties.
Je vais essayer d'aller étape par étape .Ton code est loin d’être simple
1) La premiere fonction find_char issu de Analyse.py
def find_char(file):
# Liste de tout les caractères présent dans le fichier
chars = set()
with open(file, "r", encoding="utf-8") as f:
for char in f.read():
if not (char in whitespace or char in chars):
chars.add(char)
print (chars) #fait pour faire des tests
chars.add(" ")
c = list(chars)
c.sort()
return c
Cette fonction prend en paramètre file (le fichier qu'on parcourt) .
Considérons que le fichier a 3 ligne :
a
alpha
bd
Pour chaque ligne parcouru dans ton fichier, tu enregistre dans le set nommé chars .
Vu que c'est un set , tu n'as pas besoin de vérifier que ton caractère est déjà dans le set . (Un set ne peut pas avoir élement dupliqué (caractère)
donc tu peux supprimer
or char in chars
Quand j'ai voulu voir comment il enregistrait dans le set en affichant print (chars) , les caractères était ajouter bizarement dans le set .
On pourrait penser que ca donne chars = {a,l,p,h,b,d) mais ca peut donner p,h,a,b,d,l . Ca ne change rien à la suite de ton script qui ajoute dans une list et la trie .
2)
def init_dic(car, degree):
# Retourne la iste de tout les clés du dictionnaire
loop_end = False
first_run = True
foot_counter = []
liste_keys = []
for x in range(degree):
foot_counter.append(0)
while True:
for x in range(degree - 1, -1, -1):
foot_counter[x] += 1
if foot_counter[x] > len(car) - 1:
if x == 0:
loop_end = True
break
foot_counter[x] = 0
else:
break
if loop_end:
break
if foot_counter[-1] == 1 and first_run:
foot_counter[-1] = 0
first_run = False
liste_keys.append("".join([car[x] for x in foot_counter]))
return liste_keys
Je comprends mieux pourquoi tu as un problème de mémoire
Tu est entrain de faire toute les combinaisons possible avec les caractères trouvés (liste c ) et en limitant à une longueur maximal (=Degré) .
Tu te doutes bien que cette partie pose énormément de calcul et de memoire quand tu demandes 4 dégrés .
En regardant les caractère dans la liste C , je me suis aperçu que tu fais la disctinction entre minuscule et majuscule . Ca augmente considérablement les combinaisons .
J'ai donc remplacer les majuscules en minuscules et ca fonctionne maintenant avec 4 degré
# -*- coding: utf-8 -*-
from string import whitespace
def find_char(file):
# Liste de tout les caractères présent dans le fichier
chars = set()
with open(file, "r", encoding="utf-8") as f:
for char in f.read():
if not (char in whitespace ):
chars.add(char.lower())
chars.add(" ")
c = list(chars)
c.sort()
return c
def init_dic(car, degree):
# Retourne la iste de tout les clés du dictionnaire
loop_end = False
first_run = True
foot_counter = []
liste_keys = []
for x in range(degree):
foot_counter.append(0)
while True:
for x in range(degree - 1, -1, -1):
foot_counter[x] += 1
if foot_counter[x] > len(car) - 1:
if x == 0:
loop_end = True
break
foot_counter[x] = 0
else:
break
if loop_end:
break
if foot_counter[-1] == 1 and first_run:
foot_counter[-1] = 0
first_run = False
liste_keys.append("".join([car[x] for x in foot_counter]))
return liste_keys
def correct_dic(liste_keys, file):
# Remplis le dictionnaire à partir des mots
degree = len(liste_keys[0])
dic = {x: dict() for x in liste_keys}
with open(file, "r", encoding="utf-8") as f:
f2 = " " * degree + (" " * degree).join(f.read().split("\n"))
for num in range(len(f2)):
f3 = f2[num : num + degree]
try:
dic[f3][f2[num + degree]] += 1
except IndexError:
pass
except KeyError:
try:
dic[f3][f2[num + degree]] = 1
except KeyError:
pass
except IndexError:
pass
adic = {x: y for x, y in dic.items()}
for x in adic:
if adic[x] == {}:
del dic[x]
return dic
def compile_dic(dic):
# Transforme les entrés du dictionnaire tel que "ab" : {"c": 6, "d": 3, "f": 8} en "ab" : {"c": 6, "d": 9, "f": 17}
# Pour faciliter la génération aléatoire
dico = {}
for x in dic:
dico[x] = {}
sum = 0
for y in dic[x]:
sum += dic[x][y]
dico[x][y] = sum
return dico
. Si tu veux gagner en perf, tu peux plutot que creer toute les combinaison ,enregistrer que celle qui existe vraiment
J'ai juste rajouté un bout de code à la génération à la génération de mots pour ne pas tombé sur des mots qui existe déjà. Le problème c'est que au délà de 10 ça prend trop de temps mais je ne pense pas qu'une solution existe:
Le voici:
nb_mot = int(input("Combien de mots voulez vous generer: "))
with open(fr, "r", encoding="utf-8") as f:
liste_mot = f.read()
for x in range(nb_mot):
mot = gen_word(proba)
while mot in liste_mot:
mot = gen_word(proba)
print(mot)
Du coup je passe le sujet en résolu
- Edité par rokonio 10 juin 2020 à 10:31:00
Erreur de memoire avec un gros dictionnaire
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.
Le Tout est souvent plus grand que la somme de ses parties.