Mot de passe prononçable

Résolu
Tibo3 Messages postés 2 Date d'inscription vendredi 21 mars 2025 Statut Membre Dernière intervention 22 mars 2025 - Modifié le 24 mars 2025 à 12:04
 PierrotLeFou - 25 mars 2025 à 17:34

Bonjour,

Je dois créer un mot de passe prononçable et pour le moment j'ai ce code qui doit rester le même : 

from random import *
file = open("liste_francais.txt", "r", encoding= "utf-8")
dictionnaire = {}
L=[]
liste_interdit = ["'", ";", ".", "/", ",","-","_"," "]
for mot in file.readlines():
    mot = mot.rstrip("\n")
    L.append(mot)
print(L)

for mot in L:
    for i in range(len(mot) - 1):
        lettre = mot[i].lower()
        suivante = mot[i + 1].lower()
        if lettre not in liste_interdit:
            if lettre not in dictionnaire:
                dictionnaire[lettre] = []
            dictionnaire[lettre].append(suivante)
 
def choisir_premiere_lettre():
    return chr(randint(97, 122))  
 
def generer_mot(dictionnaire, longueur=8):
    mot = choisir_premiere_lettre()
    for i in range(longueur - 1): 
        dernier_caractere = mot[-1]
        if dernier_caractere in dictionnaire:  
            k = randint(0, len(dictionnaire[dernier_caractere]) - 1)  
            mot = mot + dictionnaire[dernier_caractere][k]
        else:
            mot = mot + choisir_premiere_lettre()  
    return mot
 
def generer_mot_de_passe(dictionnaire, nombre_mots=2):
    mot_de_passe = ""
    for i in range(nombre_mots):
        if i > 0: 
            mot_de_passe += "-"
        mot_de_passe += generer_mot(dictionnaire)  
    return mot_de_passe

print(dictionnaire)
print(generer_mot_de_passe(dictionnaire))
file.close()

Je dois maintenant faire en sorte qu'il soit prononçable, mais je n'y arrive pas, je crois qu'il faut utiliser un dictionnaire de poids ou des syllabes.

S'il vous plaît

A voir également:

8 réponses

yg_be Messages postés 23535 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 27 avril 2025 Ambassadeur 1 579
Modifié le 24 mars 2025 à 12:06

Bonjour,

Pour créer un mot de passe prononçable, je ferais ainsi:

  1. créer deux listes de sons, une de voyelles, une de consonnes.  chaque son peut être composé de une ou plusieurs lettres.  éviter des sons dont la prononciation varie, et éviter d'avoir deux sons se prononçant de la même façon.   par exemple: liste de voyelles, (a, e, i, o , u, ou), liste de consonnes: (b, ss, k, t, d, p, f, l, m, n r, x,z) 
  2. pour créer un mot de passe prononçable, commencer par choisir au hasard un son d'une des listes
  3. ensuite, choisir un hasard un son de l'autre liste
  4. alterner ainsi, en prenant un son d'une liste et puis de l'autre, jusqu'à obtenir la longueur suffisante
1
mamiemando Messages postés 33662 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 mai 2025 7 849
Modifié le 24 mars 2025 à 19:07

Bonjour

Solutions simples

  • En fait l'énoncé est assez vague. À l'extrême on pourrait répondre un mot aléatoire pris dans liste_français.txt et ce serait un résultat acceptable.
  • L'algorithme d'yg_be permet de sortir du vocabulaire français (#4) et a le mérite d'être assez simple à coder. Il repose implicitement sur l'hypothèse que la langue doit alterner des suites de consonnes et de voyelles, ce qui de facto ne s'applique pas à toutes les langues. Mais c'est ce que je ferais si je devais rapidement avoir un résultat.
  • L'algorithme de Pierrot le fou pourrait aussi marcher mais oblige à hardcoder toutes les suites de lettres valides, ce qui est fastidieux, mais qu'on pourrait automatiser. C'est ce que je vais expliquer ci-dessous.

Solution proposée

Si on prend ça sous l'angle des maths, le bon objet est une chaîne de Markov. Chaque sommet de cette chaîne est en charge d'encoder le contexte dans lequel on est (les derniers caractères générés). Pour cela, on identifie chaque sommet en s'appuyant sur les n-grames observés dans liste_français.txt. L'avantage d'une approche comme celle-ci c'est qu'elle reste valable pour n'importe quel langage.

Dans ce modèle :

  • chaque sommet de la chaîne de Markov au k <= n derniers caractères générés ;
  • chaque arc connecte deux sommets a et b compte combien de fois le sous-mot a et le sous-mot b se chevauchent dans un mot w où a apparaît un caractère avant b dans w, pour tout mot w du vocabulaire.

Pour construire cette chaîne de Markov, on suit cette intuition :

  • Pour chaque mot du dictionnaire
    • Ajouter tous les préfixes de ce mot de longueur <= n
    • Ajouter tous les sous-mots de ce mot de longueur == n

... où "ajouter un {préfixe|sous-mot} de w" revient à incrémenter le poids de la paire de sous mots (w[i:j], w[i+1:j+1]). Chaque état (sommet) de la chaîne de Markov est donc identifié par le sous-mot (de longueur <= n) courant, et pour cela on peut utiliser un dictionnaire.

Voici une manière d'implémenter cet algorithme :

from collections import defaultdict
from pathlib import Path
from pprint import pprint
import numpy as np


def markov(words: list, n: int = 2) -> dict:
    """
    Makes the Markov chain.

    Args:
        words (list): The input words.
        n (int): The maximum size of the n-grams.

    Returns:
        A ``weight`` dictionary such that ``weight[a][b]``
        is the non-normalized weights from ``a`` to ``b``.
    """
    weights = defaultdict(lambda: defaultdict(int))
    for w in words:
        sw = sw_prev = ""
        for k in range(1, n):
            sw = w[:k]
            weights[sw_prev][sw] += 1
            sw_prev = sw
        for i in range(0, len(w) - n + 1):
            sw = w[i:i+n]
            weights[sw_prev][sw] += 1
            sw_prev = sw
    return weights


def load_words(filename: Path = None) -> list:
    """
    Loads a vocabulary from an input file, one
    word per line.

    Args:
        filename (Path): The input filename.
            Pass ``None`` to load
        ``"/usr/share/dict/french"`` (Linux).

    Returns:
        The loaded words.
    """
    if filename is None:
        filename = Path("/usr/share/dict/french")
    with open(filename, "r") as f:
        return [w.rstrip() for w in f.readlines()]


def walk(weights: dict, sw: str) -> str:
    """
    Chooses a next state at random.
    
    Args:
        weights (dict): The Markov chain.
        sw (str): The current state (sub-word)
            of the Markov chain.

    Returns:
        The reached state (sub-word).
    """
    elements = list(weights.keys())
    probs = np.float64(list(weights.values()))
    probs /= np.sum(probs)
    return np.random.choice(elements, 1, p=probs)[0]


def make_word(weights: dict, l: int = 10):
    """
    Generates a word at random, given a Markov chain.

    Args:
        weights (dict): The Markov chain.
        l (int): The length of the target word.

    Returns:
        A random word ``w`` such that
        ``len(w) == l`` or less in case of starvation.
    """
    sw = ""
    w = ""
    for i in range(l):
        if sw not in weights.keys():
            return w  # Starvation!!!
        sw = walk(weights[sw], sw)
        w += sw[-1]
    return w


words = load_words()
weights = markov(words, 3)

Les deux dernières lignes correspondent à la manière dont en entraîne la chaîne. Ici, on considère des tri-grames (n=3) et load_words retourne ici les mots de la langue française (dans ton cas tu pourrais charger les mots depuis ton fichier).

Une fois notre modèle est entraîné, on peut lancer des inférences. Concrètement faire une inférence revient à faire une marche aléatoire (biaisée par les poids stockés dans la chaîne de Markov) et à retourner le mot ainsi formé. Ci-dessous, je lance 12 inférences paramétrées pour obtenir des mots de 10 lettres (ou plutôt d'au plus 10 lettres, cf les problèmes de famines dont je parlerai plus tard) :

# Si on veut que les résultats soient reproductibles,
# on fixe une graine arbitraire.
np.random.seed(2)

# Inférence
for _ in range(12):
    print(make_word(weights, 10))

Résultat :

ébouteriez
mécèderass
ganteraien
félicialez
tourteronn
immophiltr
reparlemma
géminismes
lapiétéron
desterionn
ratchâmest
direspéras

Plus on va choisir des n-grames long, plus on tendra vers des mots de la langue française. En effet, si n augmente, le nombre de sommet dans la chaîne augmente significativement, mais le degré moyen de chaque sommet va aussi drastiquement chuter. Donc il faut calibrer n de sorte à ce que ça corresponde à une suite raisonnable (je pense que n=3 est un bon compromis mais je te laisse expérimenter).

Par contre, ce programme à deux limites :

  1. Fin de mot : si on n'a pas de chance, on peut stopper la marche aléatoire alors que sur le plan pratique on est au milieu d'une syllabe, et observer des fins de mots discutables (voir par exemple "desterionn", la terminaison est discutable :p).
    • Une manière de résoudre ce petit soucis (et qui serait un bon exercice), serait d'adapter le code pour indiquer quand un sommet de la chaîne de Markov correspond à un suffixe (i.e. une fin de mot).
    • Ainsi, quand on arrive à i == len(w) - l il faut être vigilant à rejeter tout successeur qui ne correspond pas à un suffixe.
  2. Famine: si on atteint un sous-mot qui n'a pas de successeur, on peut pas arriver à produire les l lettres demandées.
    • Un exemple assez simple, prenons words = ["abcdef", "abxyz"]. Avec un tel vocabulaire, on ne pourra jamais générer de mot de 10 lettres : on va systématiquement générer soit "abcdef" (puis avoir une famine) soit "abxyz" (puis avoir une famine). 
    • On pourrait se dire que si la chaîne de Markov comporte des cycles, alors on peut virtuellement générer des mots de tailles infinies. C'est vrai (prenons par exemple : words = ["abababab"] : ici peut générer une suite arbitrairement longue qui alterne de a et de b et commençant par a). Mais cela ne résout pas les cas de famine potentielle pour autant. Pour plus de détails, voir les notions d'états transients et récurrents.

Bonne chance

1
yg_be Messages postés 23535 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 27 avril 2025 1 579
24 mars 2025 à 20:31

Je ne comprends pas d'où te vient l'idée de "langue".

Pour moi, derrière l'idée de mot de passe prononçable, il y a implicitement l'idée de pouvoir restituer le mot de passe à partir de sa prononciation.  L'avantage étant qu'on peut alors retenir la prononciation sans retenir la séquence de lettres. 

0
mamiemando Messages postés 33662 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 mai 2025 7 849 > yg_be Messages postés 23535 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 27 avril 2025
25 mars 2025 à 12:42

Hello yg_be

Ton algorithme répond si on considère un langage basé sur un alphabet latin (français, anglais, allemand, italien, etc.). 

Cependant, 

1) l'espace des mots prononçables n'est pas complètement couvert : il ne considère que les alternances voyelles/consonnes, cependant il existe des séquences de voyelles et de consonnes qui sont prononçable.

2) il ne s'applique pas à n'importe quel langage en général (qu'on parle d'un langage naturel, une langue si tu préfères, ou un langage au sens de la théorie des langages):

  • Pour un autre alphabet (e.g. cyrillique) il faudrait indiquer quelles sont les voyelles.
  • L'algorithme devrait être revu pour un langue basé sur des idéogrammes ou un syllabaire. Autre exemple : prenons une langue basé sur un alphabet cyrillique, il faudrait spécifier quelles lettres correspondent aux voyelles et aux consonnes.

Le but de ma réponse était de relaxer toutes ces limitations.

0
blux Messages postés 26859 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 12 mai 2025 3 340
25 mars 2025 à 07:52

Salut,

consonne-voyelle-consonne-voyelle,consonne-voyelle, ad nauseam...

C'est le plus simple...

Maintenant, il semble y avoir des contraintes que j'ai du mal à saisir.


1
mamiemando Messages postés 33662 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 mai 2025 7 849
25 mars 2025 à 12:45

Hello blux, effectivement, ça marcherait. Ta proposition me paraît correspondre la même que celle proposée par yg_be (#4), avec les limitations (sans doute acceptables) que je soulevais dans (#10).

0
PierrotLeFou
22 mars 2025 à 01:09

Si ton code doit rester le même, alors peu de chance de réussir ...
J'aurais bien quelques modifications à suggérer, mais à quoi bon.
Les mots sont formés de suites de voyelles et de consonnes (ou groupes de voyelles et de groupes de consonnes) en alternance.
Ils peuvent commencer par une voyelle ou une consonne et se terminer de même.
Et il y a les pseudo voyelles telles que 'on', 'am', ...
Je te donne quelques exemples de groupes de consonnes valides: br, cl, gn,, phr, ...
Crées des dictionnaires de tels voyelles et groupes de voyelles, et consonnes et groupes de consonnes.
Choisis aléatoirement en alternance dans un ensemble ou l'autre.

0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
PierrotLeFou
22 mars 2025 à 02:28

Tu pourrais lire ton fichier et séparer les mots en syllabes et ajouter chaque syllabe dans un set.
Tu transformes le set en liste.
Tu choisis aléatoirement les syllabes dans la liste et tu les accumules pour former ton "pseudo-mot".
J'ai un petit algo qui fonctionne assez bien dans la plupart des cas,
sauf pour les mots d'origine étrangère comme "interactif", ou exotiques comme "aujourd'hui".
+ On avance jusqu'à la fin d'une suite de voyelles (s'il n'y en a plus, on arrête).
+ On recule jusqu'au début d'un groupe de consonnes valide (il peut ne pas y en avoir au début).
+ Si on ne trouve pas dans la liste des groupes, ce sera une simple consonne.
+ On coupe juste avant le début du groupe de consonnes.
+ On recommence après la suite de voyelles.
Liste non exhaustive de groupes de consonnes valides:
bl, br, ch, chl, chr, cl, cr, dr, fl, fr, gl, gn, gr, kr, ph, phl, phr, pl, pr, tr, vr, ...
kr: krypton

0
Tibo3 Messages postés 2 Date d'inscription vendredi 21 mars 2025 Statut Membre Dernière intervention 22 mars 2025
Modifié le 22 mars 2025 à 13:47

Merci, très bien et bien si je dois le modifier qu'il en soit ainsi mais je vous avoue que je ne comprends pas tout car je suis niveau débutant, première année de codage donc je ne sais pas trop comment créer ce que vous énoncer et il faudrait qu'il ne soit pas trop complexe 

Cordialement

0

Dans ce que je proposais, il n'y a pas tant de choses à "hardcoder". Seul un nombre relativement limité de groupes de consonnes valides.
Ensuite, il ne s'agit pas de "bien" séparer des mots français mais de trouver des syllabes qui ont un sens auditif.
Je possède un dictionnaire du même genre que celui que Tibo3 possède (environ 323500 mots).
En appliquant mon code sur ce dictionnaire, je génère environ 9300 syllabes.
Je suggère à Tibo3 qui dit être débutant de revoir la documentation sur les range et les set en Python et le slicing.
Voici un bon tutoriel sur ce dernier point:
https://zestedesavoir.com/tutoriels/582/les-slices-en-python/

Voici mon code:

#
def separerSyllabes(mot):
    # Il peut manquer des séquences ou d'autres seraient invalides, 'st' est douteux ('pastille', 'instable').
    sequences = ['bl', 'br', 'ch', 'cl', 'cr', 'dr', 'fl', 'fr', 'gl', 'gn', 'gr', 'ph', 'pl', 'pr', 'st', 'tr', 'vr', 'chl', 'chr', 'phl', 'phr']
    voyelles = "eiaouy"
    coupures = []   # Début de chaque syllabe.
    fin = len(mot)
    c = 0   # Position de la première consonne d'une séquence.
    i = 0
    while i < fin:
        if mot[i] not in voyelles:
            i += 1
        else:
            v = i   # Position de la première voyelle d'une suite.
            i += 1
            while i < fin and mot[i] in voyelles:  i += 1   # Sauter toutes les voyelles suivantes.
            # On est soit à la fin, soit sur une consonne.
            p = max(c, v-1)   # Dernière consonne d'une séquence
            for k in range(v-2, c-1, -1):   # Pour toutes les séquences de 2 consonnes ou plus, s'il y en a.
                if mot[k:v] in sequences:
                    p = k
                    break
            coupures.append(p)
            c = i
    coupures.append(fin)   # Pour terminer le dernier intervalle. Un mot sans voyelle ne marchera pas.
    return coupures
# Code principal
mots = open("liste_francais.txt", "r").read().split("\n")
S = set()
for m in mots:
    s = separerSyllabes(m)
    for i in range(len(s)-1):
        S.add(m[s[i]:s[i+1]])
print(len(S))   #print(*list(S))
0
PierrotLeFou
25 mars 2025 à 12:34

Les beautés de la langue française:
il y a 4 façons d'écrire le  o: o, au, eau, ... ô, et pourquoi pas ho
et 3 façon pour le son  c: c, k, qu
De vallée et chagrin, j'obtiens valgrin :)

0
blux Messages postés 26859 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 12 mai 2025 3 340
25 mars 2025 à 14:00

Les 12 façons de prononcer 's' :

https://www.youtube.com/watch?v=5YO7Vg1ByA8

0
PierrotLeFou > blux Messages postés 26859 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 12 mai 2025
25 mars 2025 à 15:51

Comme c'est mentionné dans ton lien, le Turc et le Finnois sont plus simples.

J'ai déjà eu une correspondante finlandaise et je connais quelques rudiments du finnois.

Dans cette langue, il n'y a pas d'équivoque.

Le finnois nattif ne comporte pas de c, q, x ou z. Mais il y a un ä (a tréma)  et ö (o tréma).

Les doubles voyelles sont prononcées comme des voyelles de durée plus longue (Exemple, la ville de Kuusamo)

0
blux Messages postés 26859 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 12 mai 2025 3 340 > PierrotLeFou
25 mars 2025 à 16:50

Sauf qu'en Finnois, la prononciation n'est pas la même qu'en français :

Jyväskylä se prononce 'yuvèskoulè' (ou à peu près...).

Le 'j' français étant une exception dans notre langue. La plupart des langues le voient en 'y' (un 'i' mouillé), (excepté en début de mot pour les anglo-saxons).

0
PierrotLeFou > blux Messages postés 26859 Date d'inscription dimanche 26 août 2001 Statut Modérateur Dernière intervention 12 mai 2025
25 mars 2025 à 17:34

Ce que je voulais dire qu'il n'y a pas d'ambiguité en finnois. Pas de doublons comme "eau" ou "o" en français. On sait toujours comment prononcer.

En passant, la fille de ma correspondante s'appelle "Jutta" qu'on prononce "Youtta" ou "Yout-ta".

Plusieurs langues ne semblent pas posséder le son "u" à la française. Le "y" finnois y correspond assez bien.

0