Entrée: "tableapplechairtablecupboard..."
beaucoup de mots
Quel serait un algorithme efficace pour diviser ce texte en une liste de mots et obtenir:
Production: ["table", "apple", "chair", "table", ["cupboard", ["cup", "board"]], ...]
La première chose qui me vient à l'esprit est de parcourir tous les mots possibles (en commençant par la première lettre) et de trouver le mot le plus long possible, continuer à partir de position=word_position+len(word)
PS
Nous avons une liste de tous les mots possibles.
Le mot «placard» peut être «tasse» et «planche», sélectionnez le plus long.
Langage: python, mais l'essentiel est l'algorithme lui-même.
['able', 'air', 'apple', 'boa', 'boar', 'board', 'chair', 'cup', 'cupboard', 'ha', 'hair', 'lea', 'leap', 'oar', 'tab', 'table', 'up']
Réponses:
Un algorithme naïf ne donnera pas de bons résultats lorsqu'il est appliqué à des données du monde réel. Voici un algorithme de 20 lignes qui exploite la fréquence relative des mots pour donner des résultats précis pour le texte de mots réels.
(Si vous voulez une réponse à votre question initiale qui n'utilise pas la fréquence des mots, vous devez préciser ce que l'on entend exactement par "mot le plus long": vaut-il mieux avoir un mot de 20 lettres et dix mots de 3 lettres, ou est il vaut mieux avoir cinq mots de 10 lettres? Une fois que vous avez choisi une définition précise, il vous suffit de changer la ligne définissant
wordcost
pour refléter le sens voulu.)L'idée
La meilleure façon de procéder est de modéliser la distribution de la sortie. Une bonne première approximation consiste à supposer que tous les mots sont distribués indépendamment. Ensuite, il vous suffit de connaître la fréquence relative de tous les mots. Il est raisonnable de supposer qu'ils suivent la loi de Zipf, c'est-à-dire que le mot de rang n dans la liste des mots a une probabilité d'environ 1 / ( n log N ) où N est le nombre de mots du dictionnaire.
Une fois que vous avez fixé le modèle, vous pouvez utiliser la programmation dynamique pour déduire la position des espaces. La phrase la plus probable est celle qui maximise le produit de la probabilité de chaque mot individuel, et il est facile de la calculer avec une programmation dynamique. Au lieu d'utiliser directement la probabilité, nous utilisons un coût défini comme le logarithme de l'inverse de la probabilité pour éviter les débordements.
Le code
que vous pouvez utiliser avec
Les resultats
J'utilise ce dictionnaire rapide et sale de 125 000 mots que j'ai créé à partir d'un petit sous-ensemble de Wikipédia.
Comme vous pouvez le voir, il est essentiellement impeccable. La partie la plus importante est de vous assurer que votre liste de mots a été formée à un corpus similaire à ce que vous rencontrerez réellement, sinon les résultats seront très mauvais.
Optimisation
L'implémentation consomme une quantité linéaire de temps et de mémoire, elle est donc raisonnablement efficace. Si vous avez besoin d'accélérations supplémentaires, vous pouvez créer une arborescence de suffixes à partir de la liste de mots pour réduire la taille de l'ensemble de candidats.
Si vous devez traiter une très grande chaîne consécutive, il serait raisonnable de diviser la chaîne pour éviter une utilisation excessive de la mémoire. Par exemple, vous pouvez traiter le texte en blocs de 10 000 caractères plus une marge de 1 000 caractères de chaque côté pour éviter les effets de limite. Cela maintiendra l'utilisation de la mémoire au minimum et n'aura presque certainement aucun effet sur la qualité.
la source
pip install wordninja
words.txt
contient "comp": `` `` $ grep "^ comp $" words.txt comp `` `et il est trié par ordre alphabétique. ce code suppose qu'il est trié selon une fréquence d'apparition décroissante (ce qui est courant pour les listes de n-grammes comme celle-ci). si vous utilisez une liste correctement triée, votre chaîne sort bien: `` `` >>> wordninja.split ('namethecompanywherebonniewasemployed whenwestarteddating') ['name', 'the', 'company', 'where', 'bonnie', ' était ',' employé ',' quand ',' nous ',' avons 'commencé', 'sortir ensemble'] `` ``Sur la base de l'excellent travail de la première réponse , j'ai créé un
pip
package pour une utilisation facile.Pour installer, exécutez
pip install wordninja
.Les seules différences sont mineures. Cela renvoie a
list
plutôt que astr
, cela fonctionne enpython3
, il inclut la liste de mots et se divise correctement même s'il y a des caractères non alpha (comme des traits de soulignement, des tirets, etc.).Merci encore à Generic Human!
https://github.com/keredson/wordninja
la source
Voici la solution utilisant la recherche récursive:
rendements
la source
En utilisant une structure de données trie , qui contient la liste des mots possibles, il ne serait pas trop compliqué de faire ce qui suit:
la source
"tableprechaun"
puis diviser par la suite"tab"
."tableprechaun"
la plus longue correspondance depuis le début est"table"
, partir"prechaun"
, qui ne peut pas être divisé en mots du dictionnaire. Vous devez donc prendre le match le plus court"tab"
en vous laissant avec un"leprechaun"
.La solution d'Unutbu était assez proche mais je trouve le code difficile à lire et il n'a pas donné le résultat attendu. La solution de Generic Human a l'inconvénient d'avoir besoin de fréquences de mots. Ne convient pas à tous les cas d'utilisation.
Voici une solution simple utilisant un algorithme Divide and Conquer .
find_words('cupboard')
retournera['cupboard']
plutôt que['cup', 'board']
(en supposant quecupboard
,cup
etboard
sont dans le dictionnaire)find_words('charactersin')
pourrait revenir['characters', 'in']
ou peut-être qu'il reviendra['character', 'sin']
(comme vu ci-dessous). Vous pouvez facilement modifier l'algorithme pour renvoyer toutes les solutions optimales.Le code:
Cela prendra environ 5 secondes sur ma machine 3GHz:
la source
La réponse de https://stackoverflow.com/users/1515832/generic-human est excellente. Mais la meilleure implémentation que j'ai jamais vue a été écrite par Peter Norvig lui-même dans son livre «Beautiful Data».
Avant de coller son code, permettez-moi d'expliquer pourquoi la méthode de Norvig est plus précise (bien qu'un peu plus lente et plus longue en termes de code).
1) Les données sont un peu meilleures - à la fois en termes de taille et de précision (il utilise un nombre de mots plutôt qu'un simple classement) 2) Plus important encore, c'est la logique derrière les n-grammes qui rend vraiment l'approche si précise .
L'exemple qu'il donne dans son livre est le problème de la division d'une chaîne «sitdown». Maintenant, une méthode non bigramme de division de chaîne considérerait p ('sit') * p ('down'), et si cela est inférieur au p ('sitdown') - ce qui sera le cas assez souvent - il ne sera PAS divisé mais nous le voudrions (la plupart du temps).
Cependant, lorsque vous avez le modèle bigramme, vous pouvez évaluer p («s'asseoir») comme un bigramme vs p («sitdown») et le premier gagne. En gros, si vous n'utilisez pas de bigrammes, cela traite la probabilité des mots que vous divisez comme indépendants, ce qui n'est pas le cas, certains mots sont plus susceptibles d'apparaître les uns après les autres. Malheureusement, ce sont aussi les mots qui sont souvent collés ensemble dans de nombreux cas et qui confondent le séparateur.
Voici le lien vers les données (il s'agit de données pour 3 problèmes distincts et la segmentation n'en est qu'un. Veuillez lire le chapitre pour plus de détails): http://norvig.com/ngrams/
et voici le lien vers le code: http://norvig.com/ngrams/ngrams.py
Ces liens existent depuis un certain temps, mais je vais quand même copier-coller la partie segmentation du code ici
la source
RuntimeError: maximum recursion depth exceeded in cmp
Voici la réponse acceptée traduite en JavaScript (nécessite node.js et le fichier "wordninja_words.txt" de https://github.com/keredson/wordninja ):
la source
Si vous précompilez la liste de mots dans un DFA (ce qui sera très lent), le temps nécessaire pour faire correspondre une entrée sera proportionnel à la longueur de la chaîne (en fait, seulement un peu plus lent qu'une simple itération sur la chaîne).
Il s'agit en fait d'une version plus générale de l'algorithme trie qui a été mentionné précédemment. Je ne le mentionne que pour une utilisation complète - pour l'instant, il n'y a pas d'implémentation DFA que vous pouvez simplement utiliser. RE2 fonctionnerait, mais je ne sais pas si les liaisons Python vous permettent de régler la taille que vous autorisez à un DFA avant qu'il ne jette simplement les données DFA compilées et effectue une recherche NFA.
la source
Il semble qu'un retour en arrière assez banal fera l'affaire. Commencez au début de la chaîne. Scannez à droite jusqu'à ce que vous ayez un mot. Ensuite, appelez la fonction sur le reste de la chaîne. La fonction renvoie "false" si elle scanne tout le chemin vers la droite sans reconnaître un mot. Sinon, renvoie le mot trouvé et la liste des mots renvoyés par l'appel récursif.
Exemple: "tableapple". Recherche «tab», puis «saut», mais aucun mot dans «ple». Aucun autre mot dans "leapple". Recherche "table", puis "app". "le" pas un mot, alors essaie la pomme, reconnaît, revient.
Pour durer le plus longtemps possible, continuez, en émettant (plutôt qu'en renvoyant) des solutions correctes; puis, choisissez l'optimum selon n'importe quel critère que vous choisissez (maxmax, minmax, moyenne, etc.)
la source
Sur la base de la solution d'Unutbu, j'ai implémenté une version Java:
Contribution:
"tableapplechairtablecupboard"
Production:
[table, apple, chair, table, cupboard]
Contribution:
"tableprechaun"
Production:
[tab, leprechaun]
la source
Pour la langue allemande, il existe CharSplit qui utilise l'apprentissage automatique et fonctionne assez bien pour des chaînes de quelques mots.
https://github.com/dtuggener/CharSplit
la source
L'expansion de la suggestion de @ miku d'utiliser a
Trie
, un append-onlyTrie
est relativement simple à mettre en œuvre danspython
:Nous pouvons ensuite construire un
Trie
dictionnaire basé sur un ensemble de mots:Ce qui produira un arbre qui ressemble à ceci (
*
indique le début ou la fin d'un mot):Nous pouvons l'incorporer dans une solution en la combinant avec une heuristique sur la façon de choisir les mots. Par exemple, nous pouvons préférer des mots plus longs aux mots plus courts:
Nous pouvons utiliser cette fonction comme ceci:
Parce que nous maintenons notre position dans le
Trie
que nous cherchons plus et des mots plus longs, on traverse letrie
au plus une fois par solution possible (plutôt que les2
temps pourpeanut
:pea
,peanut
). Le court-circuit final nous évite de marcher dans la corde dans le pire des cas.Le résultat final n'est qu'une poignée d'inspections:
Un avantage de cette solution réside dans le fait que vous savez très rapidement si des mots plus longs existent avec un préfixe donné, ce qui évite d'avoir à tester de manière exhaustive les combinaisons de séquences par rapport à un dictionnaire. Cela permet également d'accéder à un
unsolvable
réponse relativement peu coûteuse par rapport aux autres implémentations.Les inconvénients de cette solution sont une grande empreinte mémoire pour le
trie
et le coût de la constructiontrie
initiale.la source
Si vous avez une liste exhaustive des mots contenus dans la chaîne:
word_list = ["table", "apple", "chair", "cupboard"]
Utilisation de la compréhension de liste pour parcourir la liste pour localiser le mot et combien de fois il apparaît.
La fonction renvoie une
string
sortie de mots dans l'ordre de la listetable table apple chair cupboard
la source
Merci beaucoup pour l'aide sur https://github.com/keredson/wordninja/
Une petite contribution de la même chose en Java de ma part.
La méthode publique
splitContiguousWords
pourrait être intégrée avec les 2 autres méthodes de la classe ayant ninja_words.txt dans le même répertoire (ou modifiée selon le choix du codeur). Et la méthodesplitContiguousWords
pourrait être utilisée à cette fin.la source
public
méthode accepte une phrase de typeString
qui est divisée en fonction d'un premier niveau avec regex. Et pour la liste,ninja_words
il est disponible en téléchargement à partir du repo git.CA aidera
la source
Vous devez identifier votre vocabulaire - peut-être que n'importe quelle liste de mots gratuite fera l'affaire.
Une fois terminé, utilisez ce vocabulaire pour créer une arborescence de suffixes et faites correspondre votre flux d'entrée à celui-ci: http://en.wikipedia.org/wiki/Suffix_tree
la source