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.
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".
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.
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:
Tels que les chiffres reflètent la différence entre les trois phrases.
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:
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é :)
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
Si le nombre de mots ou la longueur des chaînes ont été liés, une solution de codage peut être possible.
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.
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.
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é.
Est-ce que Levenshtein distance travaillerait pour vous?
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.
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.
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 .
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
Je pense à quelque chose comme ça: