web-dev-qa-db-fra.com

Score de similarité de chaîne/hash

Existe-t-il une méthode permettant de calculer quelque chose comme le "score de similarité" général d'une chaîne? En quelque sorte, je ne compare pas deux chaînes ensemble, mais un chiffre (hachage) pour chaque chaîne qui peut ultérieurement m'indiquer que deux chaînes sont ou non similaires. Deux chaînes similaires doivent avoir des hachages similaires (proches).

Considérons ces chaînes et scores comme exemple:

Hello world                1000
Hello world!               1010
Hello earth                1125
Foo bar                    3250
FooBarbar                  3750
Foo Bar!                   3300
Foo world!                 2350

Vous pouvez voir que Bonjour tout le monde! et Hello world sont similaires et leurs scores sont proches les uns des autres.

De cette façon, la recherche des chaînes les plus similaires à une chaîne donnée serait effectuée en soustrayant le score donné des chaînes des autres scores, puis en triant leur valeur absolue.

43
Josef Sábl

Je crois que ce que vous recherchez s'appelle un Locality Sensitive Hash . Alors que la plupart des algorithmes de hachage sont conçus de manière à ce que de petites variations d’entrée entraînent des modifications importantes de la production, ces méthodes de hachage tentent l’opposé: des modifications mineures de l’entrée génèrent des modifications proportionnellement minimes de la production.

Comme d'autres l'ont mentionné, le fait d'imposer une cartographie multidimensionnelle à une cartographie bidimensionnelle pose des problèmes inhérents. C'est analogue à la création d'une carte plane de la Terre ... vous ne pouvez jamais représenter avec précision une sphère sur une surface plane. Le mieux que vous puissiez faire est de trouver un LSH optimisé pour la fonctionnalité que vous utilisez afin de déterminer si les chaînes sont "identiques".

23
DougW

La distance de Levenstein ou ses dérivés est l'algorithme que vous voulez. Faites correspondre la chaîne donnée à chacune des chaînes du dictionnaire. (Ici, si vous n'avez besoin que d'un nombre fixe de chaînes les plus similaires, vous pouvez utiliser min-heap.) Si l'utilisation de la distance de Levenstein pour toutes les chaînes du dictionnaire est trop chère, utilisez d'abord un algorithme approximatif qui exclura les mots trop éloignés de la liste des candidats. Après cela, exécutez distance levenstein sur les candidats de gauche.


Une façon de supprimer les mots distants consiste à indexer n-grammes. Prétraiter le dictionnaire en séparant chaque mot dans une liste de n-grammes. Par exemple, considérons n = 3:

(0) "Hello world" -> ["Hel", "ell", "llo", "lo ", "o w", " wo", "wor", "orl", "rld"]
(1) "FooBarbar" -> ["Foo", "ooB", "oBa", "Bar", "arb", "rba", "bar"]
(2) "Foo world!" -> ["Foo", "oo ", "o w", " wo", "wor", "orl", "rld", "ld!"]

Ensuite, créez un index de n-grammes:

" wo" -> [0, 2]
"Bar" -> [1]
"Foo" -> [1, 2]
"Hel" -> [0]
"arb" -> [1]
"bar" -> [1]
"ell" -> [0]
"ld!" -> [2]
"llo" -> [0]
"lo " -> [0]
"o w" -> [0, 2]
"oBa" -> [1]
"oo " -> [2]
"ooB" -> [1]
"orl" -> [0, 2]
"rba" -> [1]
"rld" -> [0, 2]
"wor" -> [0, 2]

Lorsque vous avez besoin de rechercher la plupart des chaînes similaires pour une chaîne donnée, vous divisez une chaîne donnée en n-grammes et ne sélectionnez que les mots Du dictionnaire qui ont au moins un n-gramme correspondant. Ceci réduit le nombre. de candidats à un montant raisonnable et vous pouvez procéder avec une chaîne de correspondance de levenstein à chacun des candidats de gauche.


Si vos chaînes sont suffisamment longues, vous pouvez réduire la taille de l'index en utilisant la technique min-hachage: Vous calculez le hachage ordinaire pour chacun des n-grammes et n'utilisez que K plus petits hachages, les autres sont jetés.

P.S. cette présentation semble être une bonne introduction à votre problème.

11
gudok

En général, cela n’est pas possible, car l’ensemble des distances d’édition entre les chaînes forme un espace métrique , mais pas une dimension fixe. Cela signifie que vous ne pouvez pas fournir un mappage entre des chaînes et des entiers qui préserve une mesure de distance entre elles.

Par exemple, vous ne pouvez pas attribuer de numéros à ces trois phrases:

  • un deux
  • un six
  • deux six

Tels que les chiffres reflètent la différence entre les trois phrases.

11
Nick Johnson

Bien que l'idée semble extrêmement douce ... Je n'ai jamais entendu parler de cela.

J'ai lu beaucoup, beaucoup de techniques, thèses et travaux scientifiques sur le sujet correction orthographique/correction typographique et les propositions les plus rapides tournent autour d'un index et de la distance de levenshtein.

Il y a des techniques assez élaborées, celle sur laquelle je travaille actuellement combine:

  • Un Trie éclaté, avec une compacité de niveau
  • Un automate de Levenshtein

Même si cela ne signifie pas qu'il est "impossible" d'obtenir un score, je pense en quelque sorte qu'il n'y aurait pas autant de recherches récentes sur les comparaisons de chaînes si cette méthode de "scoring" s'était révélée efficace.

Si jamais vous trouvez une telle méthode, je suis extrêmement intéressé :)

4
Matthieu M.

Dans un problème sans limite, aucune solution ne peut convertir une séquence de mots possible ou une séquence de caractères en un nombre unique décrivant la localité.

Imaginez la similitude au niveau du personnage

stops
spots

hello world
world hello

Dans les deux exemples, les messages sont différents, mais les caractères du message sont identiques, de sorte que la mesure doit contenir une valeur de position ainsi qu'une valeur de caractère. (char 0 == 'h', char 1 == 'e' ...)

Puis comparez les messages similaires suivants

hello world
Ello world

Bien que les deux chaînes soient similaires, elles peuvent différer au début ou à la fin, ce qui rend la mise à l'échelle par position problématique.

Dans le cas de

spots
stops

Les mots ne diffèrent que par la position des caractères, une position est donc importante.

Si les chaînes suivantes sont similaires

 yesssssssssssssss
 yessssssssssssss

Ensuite, vous avez une forme de paradoxe. Si vous ajoutez 2 caractères s à la deuxième chaîne, la distance à laquelle elle se trouvait depuis la première chaîne devrait être partagée, mais elle devrait être distincte. Cela peut être répété en obtenant des chaînes de plus en plus longues, qui doivent toutes être proches des chaînes de plus en plus courtes et longues. Je ne vois pas comment y arriver.

En général, cela est traité comme un problème multidimensionnel - briser la chaîne en un vecteur

[ 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' ]

Mais les valeurs du vecteur ne peuvent pas être 

  • représenté par un numéro de taille fixe, ou 
  • donner une bonne mesure de différence de qualité.

Si le nombre de mots ou la longueur des chaînes ont été liés, une solution de codage peut être possible.

Valeurs bornées

En utilisant quelque chose comme la compression arithmétique, une séquence de mots peut être convertie en un nombre à virgule flottante qui représente la séquence. Cependant, cela traiterait les éléments plus tôt dans la séquence comme étant plus importants que le dernier élément de la séquence.

solution d'exploration de données

Si vous acceptez le fait que le problème est de grande dimension, vous pouvez alors stocker vos chaînes dans un arbre métrique wikipedia: arbre métrique . Cela limiterait votre espace de recherche sans résoudre votre solution "à numéro unique".

J'ai le code pour tel à github: clustering

Les articles rapprochés doivent être stockés ensemble dans une partie de l’arbre, mais il n’ya vraiment aucune garantie. Le rayon des sous-arbres est utilisé pour élaguer l'espace de recherche.

Modifier la distance ou la distance de Levenshtein

Ceci est utilisé dans une extension sqlite pour effectuer une recherche de similarité, mais sans solution de numéro unique, il détermine le nombre de modifications modifiant une chaîne en une autre. Cela donne alors un score qui montre une similarité.

2
mksteve

Est-ce que Levenshtein distance travaillerait pour vous?

2
Karl Knechtel

Votre idée ressemble à ontologie mais s'applique à des phrases entières. Plus deux phrases sont similaires, plus elles sont proches dans le graphique (en supposant que vous utilisez des arêtes pondérées). Et vice-versa: les expressions non similaires sont très éloignées les unes des autres.

Une autre approche consiste à utiliser la transformation de Fourier pour obtenir une sorte d'index pour une chaîne donnée (ce ne sera pas un nombre unique, mais toujours). Vous trouverez peut-être un peu plus dans ce document .

Et une autre idée, basée sur la distance de Levenshtein: vous pouvez comparer des n-grammes qui vous donneront un indice de similarité pour deux phrases données - plus elles sont similaires, plus la valeur est proche de 1. Cela peut être utilisé pour calculer la distance dans le sens inverse. graphique. a écrit un article à ce sujet il y a quelques années, si vous voulez, je peux le partager.

Quoi qu'il en soit: malgré le fait que je ne connaisse pas la solution exacte, je suis également intéressé par ce que vous proposez.

1
Przemek Kryger

Utilisez peut-être PCA , où la matrice est une liste des différences entre la chaîne et un alphabet fixe (à la ABCDEFGHI ...). La réponse pourrait être simplement la longueur de la composante principale.

Juste une idée.

PCA prêt à fonctionner en C #

1
smirkingman

Il est peu probable que l'on obtienne un nombre assez petit de deux phrases qui, comparées, donnent une indication pertinente de la similitude de leurs phrases initiales.
Une des raisons est que le nombre donne une indication dans une dimension alors que les phrases évoluent dans deux dimensions: longueur et intensité.

Le nombre pourrait évoluer aussi bien en longueur qu'en intensité mais je ne suis pas sûr que ça va aider beaucoup.

En deux dimensions, vous feriez mieux de regarder une matrice dans laquelle des propriétés telles que déterminant (une sorte de dérivé de la matrice) pourraient donner une idée approximative de la phrase trend .

0
Ring Ø

Dans Traitement du langage naturel nous avons un appel de chose Distance minimale d'édition (également appelé Distance de Levenshtein)
Il est défini fondamentalement comme la plus petite quantité d'opération nécessaire pour transformer chaîne1 en chaîne2
Opérations incluses Insertion, suppression, subsitution , chaque opération se voit attribuer un score que vous ajoutez à la distance.
Pour résoudre votre problème, l’idée est de calculer le MED de la chaîne que vous avez choisie, de l’ensemble des autres chaînes, de trier cette collection et de choisir la n-ième première chaîne distante.
Par exemple: 

{"Hello World", "Hello World!", "Hello Earth"}
Choosing base-string="Hello World"  
Med(base-string, "Hello World!") = 1  
Med(base-string, "Hello Earth") = 8  
1st closest string is "Hello World!"

Cela a donné un score à chaque chaîne de votre collection de chaînes.
C # Implementation (Add-1, Deletion-1, Subsitution-2) 

public static int Distance(string s1, string s2)
{
    int[,] matrix = new int[s1.Length + 1, s2.Length + 1];

    for (int i = 0; i <= s1.Length; i++)
        matrix[i, 0] = i;
    for (int i = 0; i <= s2.Length; i++)
        matrix[0, i] = i;

    for (int i = 1; i <= s1.Length; i++)
    {
        for (int j = 1; j <= s2.Length; j++)
        {
            int value1 = matrix[i - 1, j] + 1;
            int value2 = matrix[i, j - 1] + 1;
            int value3 = matrix[i - 1, j - 1] + ((s1[i - 1] == s2[j - 1]) ? 0 : 2);

            matrix[i, j] = Math.Min(value1, Math.Min(value2, value3));
        }
    }

    return matrix[s1.Length, s2.Length];
}

Complexité O (n x m) où n, m est la longueur de chaque chaîne
Plus d’informations sur la distance minimale de montage peuvent être trouvées ici

0
rocketspacer

Je pense à quelque chose comme ça: 

  1. supprimer tous les caractères autres que Word
  2. appliquer soundex
0
alpha-mouse