J'essaie de trouver un algorithme DP approprié pour simplifier une chaîne. Par exemple, j'ai une chaîne Le but est d'obtenir tous les caractères simples pouvant être reçus de la chaîne donnée à l'aide de ces règles. Pour cet exemple, ce sera Les règles sont toujours A B A B code> et une liste de règles p>
A B -> B CODE> LI>
A B -> C CODE> LI>
B a -> A code> li>
C C -> B code> li>
ol>
b, c code>. La longueur de la chaîne donnée peut comporter jusqu'à 200 symboles. Pourriez-vous inviter un algorithme efficace? P>
2 -> 1 code>. J'ai une idée de créer un arbre, la racine est donnée de la chaîne et chaque enfant est une chaîne après une transformation, mais je ne sais pas si c'est le meilleur moyen. P>
3 Réponses :
Pour un problème DP, vous devez toujours comprendre comment vous pouvez construire la réponse à un gros problème en termes de sous-problèmes plus petits. Supposons que votre fonction en python, cela peut être implémenté comme: p> Vérification rapide: p> pour rendre cela assez rapide pour les grandes chaînes (éviter le comportement exponentiel ), vous devriez utiliser un Memoze Decorator a>. Il s'agit d'une étape critique dans la résolution des problèmes DP, sinon vous faites un calcul de force brute. Une autre vitesse de faible vitesse peut être obtenue en revenant de la fonction dès que Analyse du temps d'exécution: pour une entrée de longueur simplifie code> appelée avec une entrée de longueur
n code>. Il existe
N-1 code> façons de diviser l'entrée dans une première et une dernière partie. Pour chacune de ces scissions, vous devez appeler récursivement votre fonction
simplifier code> sur la première partie et la dernière partie. La réponse finale pour l'entrée de longueur
n code> est l'ensemble de toutes les combinaisons possibles des réponses pour la première et pour la dernière partie autorisée par les règles.
possible_chars == SET ('ABC') code>, car à ce stade, vous êtes déjà sûr que vous pouvez générer tous les résultats possibles. < / p>
n code>, il existe 2 sous-chaînes de longueur
n-1 code>, 3 sous-chaînes de longueur
n -2 code>, ... n Substrings de longueur 1, pour un total de
O (n ^ 2) code> sous-productions. En raison de la mémoisation, la fonction est appelée au plus une fois pour chaque sous-émérule. Le temps de fonctionnement maximal pour un seul sous-problème est
O (n) code> en raison du
pour i in gamme (len (s)) code>, donc l'heure de fonctionnement globale est au maximum.
O (n ^ 3) code>. p> p>
Je ne suis pas bon chez Python et j'ai plusieurs questions. Est-ce que tête = s [: i]; Tail = S [I:] Code> Split String en deux parties sur l'index i ou prenez i Char's des deux extrémités de la chaîne? Et qu'est-ce qui fait
possible_Chars.UPDate (règles.get (C1 + C2, SET ())) CODE>?
1) c'est la notation de la tranche, s [: i] == s [0: i] code>, ce qui correspond au premier
i code> caractères et
s [i :] == s [i: len (s)] code> prend tout sauf le premier
i code> caractères. Donc, pour une entrée
abcd code>, la boucle sur
i code> diviserait cela en
tête, queue = 'A', 'BCD' code>,
tête, queue = 'ab', 'cd' code> et
tête, queue = 'abc', 'd' code>.
2) C'est une manière compacte pour: Concaténate caractères C1 code> et
C2 code>, recherchez l'ensemble possible des réponses de cette combinaison dans le dictionnaire des règles et définissez enfin
possible_chars code> le Union d'elle-même et ensemble possible de réponses de la des règles. Le
obtenez (clé, défaut) code> méthode sur un dictionnaire lève la touche dans un dictionnaire et s'il n'est pas trouvé, renvoie une valeur par défaut. J'utilise cela pour renvoyer un ensemble vide
défini () code> au cas où la combinaison à 2 lettres n'est pas dans les règles, l'union avec cela ne fait rien.
Un moyen plus explicite serait d'écrire si C1 + C2 dans les règles: possible_Chars = possible_Chars.Union (Règles [C1 + C2]) code>.
Pour la complétude du code, je pense qu'il est bon d'ajouter une définition de Memoize code>. Ou il est pris de la bibliothèque de Python que je ne suis pas au courant?
Laisse n - longueur de la chaîne donnée et R - Nombre de règles.
Développer un arbre de haute direction donne une complexité informatique O (nr ^ n) dans le pire des cas (chaîne d'entrée de type Preuve: p> racine de l'arbre a (N-1) R Enfants , qui ont (N-1) r ^ 2 enfants, ..., qui ont (N-1) r ^ n enfants (Leafs). Donc, la complexité totale est O ((N-1) R + (N-1) R ^ 2 + ... (N-1) R ^ N) = O (N (1 + R ^ 2 + ... + R ^ n)) = (en utilisant Théorème binomial ) = O (N (R + 1 ) ^ N) = O (nr ^ n). P> Mise en œuvre Java récursive de cette approche naïve: p> bas Swinckels's O (RN ^ 3) Mise en œuvre Java (avec sortie dans les deux approches: p> AAA ... code> et des règles
aa -> a code>). p>
Hashmap code> comme cache de mémoisation): p>
[b, c]
Je ne parle pas très couramment Java, mais votre deuxième implémentation utilise-t-elle la notice d'utilisation (c'est-à-dire la mise en cache du résultat pour les intrants fixes)? Sinon, ce n'est pas O (n ^ 3) code>, mais exponentiel.
Merci, les deux algorithmes fonctionnent parfaitement, mais j'ai besoin d'un peu d'aide pour l'optimisation. Le deuxième algorithme est rapide, mais j'en ai besoin pour être un peu plus rapide. Peut-être que vous pouvez me donner des conseils sur l'optimisation, par exemple, utilisez une structure de données plus rapide ou une boucle plus efficace?
@ user2875945 Je n'ai pas implémenté Bas Swinckels "Speed Hack" (regardez son code), cela devrait aider à la performance.
Si vous lisez ces règles de droite à gauche, elles ressemblent exactement aux règles d'une grammaire sans contexte et ont essentiellement la même signification. Vous pouvez appliquer un algorithme d'analyse ascendant comme le Authley Algorithm à vos données, ainsi qu'un règle de départ; Quelque chose comme , puis examinez simplement la forêt d'analyse pour la chaîne la plus courte de Vous pouvez également produire des forêts d'analyse lorsque analyse de dérivés . Vous pourriez être capable de les vérifier efficacement pour les chaînes courtes de Démarrer code> s. Bien sûr, le pire cas reste sûr, mais Earley est assez efficace, ces jours-ci. P>
Démarrer code> s. P> p>
La règle a-t-elle toujours 2 à 1 cartographie? Quelle est votre meilleure solution? la chaîne la plus courte? la chaîne avec la plupart des mêmes lettres?
Vous devez nous montrer des efforts. Avez-vous envisagé comment vous aborderiez ce problème? Donnez-nous vos pensées. Essayez quelque chose. I> Lorsque vous êtes coincé, postez-nous ce que vous avez essayé et expliquez quel est le problème.
Et que voulez-vous dire avec DP ?
@Bas Probablement la programmation dynamique
@Dialecticus Je le pensais, mais je déteste deviner. Il y a même un tag pour ça ...
@BAS DP est la programmation dynamique. Désolé pour une erreur, j'ai déjà ajouté la balise.
@Cerkiewny Oui, la règle est toujours 2 à 1. Il n'y a pas de meilleure solution, objectif est d'obtenir toutes les solutions possibles.
Pourquoi
A B -> .. code> deux fois dans votre liste? De plus: chaque caractère de sortie est-il écrit directement, c'est-à-dire non traité, ou le gardez-vous comme le prochain caractère «premier» et redémarrez-le au sommet?
@Jongware 'A b' peut être transformé en 'B' ou à 'C', il existe donc deux règles. Je ne suis pas sûr que j'ai bien compris votre deuxième question, mais ce n'est pas grave si «B 'peut être reçu de différentes manières, seule une chose qui compte est que cela puisse être reçu.
Dans votre exemple
abab code>, la première transformation est la règle 1:
AB -> B code> (ou la règle 2:
AB -> C code> - mais comment vous savez lequel utiliser?). Cela mène-t-il à la nouvelle chaîne
bab code> (et utilisez la règle 3) ou est
B code> immédiatement "consommé", laissant
ab code> comme entrée suivante (et utiliser la règle 1 à nouveau)?
@Jongware à l'aide de la règle 1 sur les premières lettres de
A B A B CODE> mène à la chaîne
B A B code>
Cyky ne fonctionnerait-il pas pour cela essentiellement non modifié?