donné une chaîne longue Faire de la manière évidente (énumérer chaque sous-chaîne de L) nécessite O ( Edit: Le boyer-Moore modifié serait une bonne idée, sauf que mon alphabet n'est que de 4 lettres (ADN). P> l code> et une chaîne plus courte s code> (la contrainte est que l code> .Longueur doit être> = s Code> .length), je souhaite trouver la distance minimale de Hamming entre S code> et toute sous-chaîne de l code> avec une longueur égale à s code>. longueur. Appelons la fonction de ce minhamming () code>. Par exemple, P>
MINHAMMING (ABCDEFGHIJ, CDEFGG) == 1 code>. P>
MINHAMMING (ABCDEFGHIJ, BCDGHI) == 3 code>. P>
S code> .length * l code> .Longueur) heure. Y a-t-il un moyen intelligent de le faire dans le temps subliné? Je cherche le même l code> avec plusieurs chaînes code> S code>, ainsi que du prétraitement compliqué sur l code> une fois est acceptable. P>
3 Réponses :
Vous êtes coincé aussi loin que Big-O est concerné. À un niveau fondamental, vous devrez tester si chaque lettre de la cible correspond à chaque lettre admissible dans la sous-chaîne. P>
Heureusement, cela est facilement parallélisé. P>
Une optimisation que vous pouvez appliquer est de conserver un nombre de décomptes de fonctionnement de la position actuelle. Si c'est plus grand que la distance de frappe la plus basse jusqu'à présent, vous pouvez évidemment passer à la prochaine possibilité. P>
-1 Désolé. En fait, des algorithmes plus rapides existent pour ce problème (voir mon post) bien qu'ils ne soient pas nécessairement évidents.
Je viens de creuser une vieille implémentation de python de BOYER-MOORE J'avais couché et j'ai modifié la boucle correspondante (où le texte est comparé au motif). Au lieu de briser dès que la première inadéquation se trouve entre les deux cordes, comptez simplement le nombre de désactivités, mais Si la chaîne ne correspondait pas complètement à ce stade, revenez au point de la première inadéquation en restaurant les valeurs. Cela garantit que la plus petite distance est réellement trouvée. P> Toute la mise en œuvre est plutôt longue (~ 150LOC), mais je peux le poster sur demande. L'idée principale est décrite ci-dessus, tout le reste est standard Boyer-Moore. P> Un autre moyen de faire accélérer les choses est la prétraitement du texte pour avoir un index sur les positions de caractère . Vous ne voulez que commencer à comparer à des positions où au moins une seule correspondance entre les deux chaînes se produit, sinon la distance de Hamming est | S | trivialement. p> Cette méthode crée simplement un dictionnaire qui mappe chaque caractère dans le texte à la liste triée de ses occurrences. P> La boucle de comparaison est plus ou moins Inchangé à naïf Prétraitement sur le texte H1>
O (mn) code> Approche, à part le fait que nous n'augmentons pas la position à laquelle la comparaison est démarrée par 1 chaque fois, mais sur la base des positions de caractère: P> def find_next_pos(pattern, pos, i):
smallest = sys.maxint
for idx, c in enumerate(pattern):
if c in pos:
x = bisect.bisect_left(pos[c], i + idx)
if x < len(pos[c]):
smallest = min(smallest, pos[c][x] - idx)
return smallest
Oh bien, et je suis désolé, tous les exemples de code sont à Python, mais c'est juste la langue que je suis le plus à l'aise. N'hésitez pas à demander des éclaircissements!
Personnellement, je pense que les gens devraient toujours poster du code dans la langue dont ils sont le plus à l'aise. Si rien d'autre, il enseigne aux autres un peu de cette langue.
+1 Pour nettoyer les échantillons de code, et j'avais encore +1 (si autorisé) pour le bigram ou plus grand avec une petite idée de l'alphabet. 4-grammes d'ADN serait similaire à 8 bits d'octets, après tout. Vous avez juste besoin de penser à la transformation de votre N-gram à la séquence réelle un peu.
Peut-être surprenant, ce problème exact peut être résolu Voici un fichier PDF librement disponible d'un document de Donald Benson décrivant comment cela fonctionne: P>
Résumé: strong> Convertissez chacune de vos chaînes update 21/7/2009 strong>: mis à jour pour mentionner que la complexité temporelle dépend également de manière linéaire de la taille de l'alphabet, car une paire de vecteurs indicateurs distinct doit être utilisée pour chaque caractère de l'alphabet . p> L code> et | a | code> est la taille de l'alphabet. P>
S code> et l code> dans plusieurs em> vecteurs em> (un par caractère, donc 4 dans le cas de l'ADN), puis Convolvez Vecteurs correspondants pour déterminer les chiffres de correspondance pour chaque possible alignement. L'astuce est que la convolution dans le domaine "temps", qui nécessite habituellement une heure O (n ^ 2), peut être mise en œuvre à l'aide de la multiplication dans le domaine "fréquence", qui nécessite juste O (n) temps, plus le temps nécessaire pour convertir entre les domaines et le dos à nouveau. L'utilisation de la FFT Chaque conversion prend juste une heure (Nlog N), de sorte que la complexité du temps global est O (| A | Nlog N). Pour une vitesse la plus grande, les champs finis em> sont utilisés, ce qui nécessite uniquement des arithmétiques entier. P>
S code> et l code> Cet algorithme est clairement une énorme victoire de performances sur l'algorithme de O (mn). > | S | code> et | l | code> devient grand, mais otoh si s code> est généralement plus court que log | l | code> (par exemple Lors de la requête d'une grande dB avec une petite séquence), alors évidemment cette approche ne fournit aucune vitesse. p>
Pas pratique pour ma situation B / C S est très faible par rapport à L, mais une réponse assez intéressante pour mériter une uppote de toute façon.
Mis à jour de mentionner que la complexité de temps dépend également de manière linéaire de la taille de l'alphabet.
Merci Daniel! Oui, les FFT sont l'une de ces choses qui me semblent un peu magiques pour moi - comme si vous ne devriez pas être capable de calculer quelque chose qui est rapide :) Autres choses dans cette catégorie si impossible pour moi sont le fait que suffixez les arbres / Les tableaux peuvent être construits et interrogés en temps linéaire :)
Soigné! @dsimcha: si s est très petit - aussi court que journal | L | lettres - alors pourquoi déranger? Notez que vous avez une borne inférieure Ω (| L |) (vous devez regarder toutes les lettres de L) ... vous êtes donc dans un état où | L | est assez petit mais | L | * Log | L | est trop grand?
Si votre contrainte est (Longueur> = S.Length) si S est plus courte que L?
Combien est "plusieurs différents"? Quelle est la taille de | L | et | s |, relativement / absolument? ~~~~