7
votes

Le moyen le plus rapide de trouver une distance minimale de Hamming à toute sous-chaîne?

donné une chaîne longue l et une chaîne plus courte s (la contrainte est que l .Longueur doit être> = s .length), je souhaite trouver la distance minimale de Hamming entre S et toute sous-chaîne de l avec une longueur égale à s . longueur. Appelons la fonction de ce minhamming () . Par exemple,

MINHAMMING (ABCDEFGHIJ, CDEFGG) == 1 .

MINHAMMING (ABCDEFGHIJ, BCDGHI) == 3 .

Faire de la manière évidente (énumérer chaque sous-chaîne de L) nécessite O ( S .length * l .Longueur) heure. Y a-t-il un moyen intelligent de le faire dans le temps subliné? Je cherche le même l avec plusieurs chaînes S , ainsi que du prétraitement compliqué sur l une fois est acceptable.

Edit: Le boyer-Moore modifié serait une bonne idée, sauf que mon alphabet n'est que de 4 lettres (ADN).


2 commentaires

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? ~~~~


3 Réponses :


-1
votes

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.

Heureusement, cela est facilement parallélisé.

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é.


1 commentaires

-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.



2
votes

MODIFIÉ BOYER-MOORE

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 N'oubliez pas la première désactivation forte>: p> xxx pré>

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>

Prétraitement sur le texte H1>

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> xxx pré>

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 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


3 commentaires

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.



16
votes

Peut-être surprenant, ce problème exact peut être résolu en juste O (| A | Nlog n) Heure à l'aide de FFTS Fourier Transforms (FFTS), où n est la longueur de la plus grande séquence L et | a | est la taille de l'alphabet.

Voici un fichier PDF librement disponible d'un document de Donald Benson décrivant comment cela fonctionne:

  • Méthodes de Fourier pour l'analyse de la biosequence (Donald Benson, < EM> Recherche d'acides nucléiques 1990 Vol. 18, p. 3001-3006)

    Résumé: Convertissez chacune de vos chaînes S et l dans plusieurs vecteurs (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 sont utilisés, ce qui nécessite uniquement des arithmétiques entier.

    Remarque: pour arbitraire S et l Cet algorithme est clairement une énorme victoire de performances sur l'algorithme de O (mn). > | S | et | l | devient grand, mais otoh si s est généralement plus court que log | l | (par exemple Lors de la requête d'une grande dB avec une petite séquence), alors évidemment cette approche ne fournit aucune vitesse.

    update 21/7/2009 : 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 .


4 commentaires

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?