Подтвердить что ты не робот

Что является хорошим показателем для принятия решения о том, что 2 строки являются "достаточно похожими"

Я работаю над очень грубым алгоритмом первого проекта, чтобы определить, насколько похожи 2 строки. Я также использую Levenshtein Distance для вычисления расстояния редактирования между строками.

То, что я делаю в настоящее время, в основном принимает общее количество изменений и делит его на размер более крупной строки. Если это значение ниже некоторого порога, в настоящее время в случайном порядке установлено значение 25%, то они "достаточно похожи".

Однако это абсолютно произвольно, и я не думаю, что это очень хороший способ рассчитать сходство. Существует ли какое-то математическое уравнение или метод вероятности/статистики для получения данных о расстоянии Левенштейна и использования его, чтобы сказать "да, эти строки достаточно похожи на количество внесенных изменений и размер строк"?

Кроме того, ключевым моментом здесь является то, что я использую произвольный порог, и я бы предпочел не делать этого. Как я могу вычислить этот порог, а не назначать его, чтобы я мог смело сказать, что 2 строки имеют "достаточно похожий" ?

UPDATE

Я сравниваю строки, представляющие трассировку стека Java. Причина, по которой я хочу это сделать, - группировать кучу заданных трассировок стека по подобию и использовать его как фильтр для сортировки "stuff":) Эта группировка важна для причины более высокого уровня, которую я не могу точно публиковать публично.


До сих пор мой алгоритм (псевдокод) примерно соответствовал строкам:

/*
 * The input lists represent the Strings I want to test for similarity. The
 * Strings are split apart based on new lines / carriage returns because Java
 * stack traces are not a giant one-line String, rather a multi-line String.
 * So each element in the input lists is a "line" from its stack trace.
 */
calculate similarity (List<String> list1, List<String> list2) {

    length1 = 0;
    length2 = 0;
    levenshteinDistance = 0;

    iterator1 = list1.iterator();
    iterator2 = list2.iterator();

    while ( iterator1.hasNext() && iterator2.hasNext() ) {

        // skip blank/empty lines because they are not interesting
        str1 = iterator1.next();    length1 += str1.length();
        str2 = iterator2.next();    length2 += str2.length();

        levensteinDistance += getLevenshteinDistance(str1, str2);
    }

    // handle the rest of the lines from the iterator that has not terminated

    difference = levenshteinDistance / Math.max(length1, length2);

    return (difference < 0.25) ? true : false; // <- arbitrary threshold, yuck!
}
4b9b3361

Ответ 1

Как насчет использования подобия косинуса? Это общий метод оценки сходства между двумя текстами. Он работает следующим образом:

Возьмите все буквы обеих строк, постройте таблицу следующим образом:

Letter | String1 | String2

Это может быть простая хеш-таблица или что-то еще.

В столбце письма помещается каждая буква, а в столбцах строки помещается их частота внутри этой строки (если буква не отображается в строке, значение равно 0).

Это называется сходством косинуса, потому что вы интерпретируете каждый из двух столбцов строки как векторы, где каждый компонент - это число, связанное с буквой. Затем вычислим косинус "угла" между векторами как:

C = (V1 * V2) / (|V1| * |V2|)

Числитель - это точечное произведение, то есть сумма произведений соответствующих компонентов, а знаменатель - произведение размеров векторов.

Как близко C к 1 дает вам, как похожи строки.

Это может показаться сложным, но это всего лишь несколько строк кода, как только вы поймете эту идею.

Посмотрим на пример: рассмотрим строки

s1 = aabccdd
s2 = ababcd

Таблица выглядит так:

Letter a b c d
s1     2 1 2 2
s2     2 2 1 1

И таким образом:

C = (V1 * V2) / (|V1| * |V2|) = 
(2 * 2 + 1 * 2 + 2 * 1 + 2 * 1) / (sqrt(13) * sqrt(10)) = 0.877

Итак, они "очень похожи".

Ответ 2

Трассировка стека в формате, пригодном для синтаксического анализа. Я просто разбирал бы трассировки стека, используя библиотеку синтаксического анализа, а затем вы можете извлечь любой семантический контент, который хотите сравнить.

Алгоритмы схожести будут медленнее и труднее отлаживать, когда строки не сравниваются, как вы ожидаете.

Ответ 3

Вот мой взгляд на это - просто долгая история, чтобы рассмотреть и не обязательно ответить на вашу проблему:

В прошлом я сделал что-то похожее, где я попытался бы определить, плагиат ли кто-то, просто переставляя предложения при сохранении такого же сообщения.

1 "дети должны играть, пока мы едим обед"
2 ", пока мы едим обед, дети должны играть"
3 "мы должны есть детей, пока мы играем"

Таким образом, levenshtein не будет иметь большого значения здесь, потому что он линейный, и каждый из них будет значительно отличаться. Стандартная разница прошла бы тест, и ученик избежал бы преступления.

Итак, я сломал каждое слово в предложениях и переформулировал предложения как массивы, затем сравнил друг друга, чтобы определить, существовало ли слово в каждом массиве и где оно было по отношению к последнему. Затем каждое слово проверяет следующее в массиве, чтобы определить, были ли последовательные слова, например, в моих примерах предложений выше строк 1 и 2. Поэтому, если бы были последовательные слова, я бы составил строку каждой последовательности, общую для каждого массива, а затем попытался найти различия в остальных словах. Чем меньше оставшихся слов, тем больше вероятность, что они просто наполнители, чтобы они казались менее плагиатными.

", пока мы едим обед, я думаю, что дети должны играть"

Затем "Я думаю" оценивается и считается наполнителем на основе словарного словаря - эту часть трудно описать здесь.

Это был сложный проект, который сделал намного больше, чем то, что я описал, а не простой кусок кода, с которым я могу легко поделиться, но вышеприведенную идею не так сложно реплицировать.

Удачи. Меня интересует, что другие члены SO могут сказать о вашем вопросе.

Ответ 4

Поскольку расстояние Левенштейна никогда не превышает длину более длинной строки, я бы, конечно, изменил знаменатель от (length1 + length2) до Math.max(length1, length2). Это нормализовало бы метрику в пределах от нуля до единицы.

Теперь невозможно ответить на то, что "достаточно достаточно" для ваших нужд на основе предоставленной информации. Я лично стараюсь избегать ступенчатых функций, как у вас, с обрезкой 0,25, предпочитая непрерывные значения с известного интервала. Возможно, лучше было бы передавать непрерывные значения "сходства" (или "расстояния" ) в алгоритмы более высокого уровня вместо преобразования этих значений в двоичные?