PHP: использование levenshtein distance для соответствия словам

77
8

Я читал и тестировал некоторые примеры в php levenshtein. Сравнение сравнения $ input и $ words

$input = 'hw r u my dear angel';

// array of words to check against
$words = array('apple','pineapple','banana','orange','how are you',
'radish','carrot','pea','bean','potato','hw are you');

выходы

Input word: hw r u my dear angel
Did you mean: hw are you?

сравнение, удаление hw - это вы в массиве.

$input = 'hw r u my dear angel';

// array of words to check against
$words = array('apple','pineapple','banana','orange','how are you',
'radish','carrot','pea','bean','potato');

во втором удалении hw вы находитесь в выводах массива

Input word: hw r u my dear angel
Did you mean: orange?

где в similar_text()

 echo '<br/>how are you:'.similar_text($input,'how are you');
echo '<br/>orange:'.similar_text($input,'orange');
echo '<br/>hw are you:'.similar_text($input,'hw are you');

how are you:6
orange:5
hw are you:6

во втором сравнении, почему он выводит оранжевый, когда у вас есть также 6 похожих текстов, таких как hw? Есть ли способ улучшить или улучшить метод об этом? Кроме того, я сохраняю все возможные входные данные в базе данных. должен ли я запросить его и сохранить в array затем использовать foreach чтобы получить levenshtein distance? но это будет медленно, если у вас есть миллионы.

КОД

  <?php
// input misspelled word
$input = 'hw r u my dear angel';

// array of words to check against
$words = array('apple','pineapple','banana','orange','how are you',
'radish','carrot','pea','bean','potato','hw are you');

// no shortest distance found, yet
$shortest = -1;

$closest = closest($input,$words,$shortest);

echo "Input word: $input<br/>";
if ($shortest == 0) {
echo "Exact match found: $closest\n";
} else {
echo "Did you mean: $closest?\n";
}
echo '<br/><br/>';

$shortest = -1;
$words = array('apple','pineapple','banana','orange','how are you',
'radish','carrot','pea','bean','potato');
$closest = closest($input,$words,$shortest);
echo "Input word: $input<br/>";
if ($shortest == 0) {
echo "Exact match found: $closest\n";
} else {
echo "Did you mean: $closest?\n";
}

echo '<br/><br/>';
echo 'Similar text';
echo '<br/>how are you:'.similar_text($input,'how are you');
echo '<br/>orange:'.similar_text($input,'orange');
echo '<br/>hw are you:'.similar_text($input,'hw are you');

function closest($input,$words,&$shortest){
// loop through words to find the closest
foreach ($words as $word) {

// calculate the distance between the input word,
// and the current word
$lev = levenshtein($input, $word);

// check for an exact match
if ($lev == 0) {

// closest word is this one (exact match)
$closest = $word;
$shortest = 0;

// break out of the loop; we've found an exact match
break;
}

// if this distance is less than the next found shortest
// distance, OR if a next shortest word has not yet been found
if ($lev <= $shortest || $shortest < 0) {
// set the closest match, and shortest distance
$closest = $word;
$shortest = $lev;
}

}
return $closest;
}
?>

спросил(а) 2021-01-25T12:26:21+03:00 4 месяца, 4 недели назад
1
Решение
99

Прежде всего, не имеет значения, какие выходы similar_text(), потому что он использует другой алгоритм для вычисления сходства между строками.

Давайте попробуем понять, почему levenstein() думает, что hw ru мой дорогой член ближе к апельсину, чем к "как вы. В Википедии есть хорошее определение того, что такое расстояние Левенштейна.

Неформально расстояние Левенштейна между двумя словами - это минимальное количество односимвольных изменений (вставка, удаление, подстановка), необходимых для смены одного слова на другое.

Теперь давайте посчитаем, сколько изменений мы должны сделать, чтобы изменить hw ru мой дорогой ангел на оранжевый.

hw ru мой дорогой ангел → hw ru my dear ange (удаление последнего персонажа) hw ru my my ange → hw ru my dearange (удаление последнего пробела) hw ru my dearange → arange (удаление первых 12 символов) arange → orange (замена a на o)

Так что требуется 1 + 1 + 12 + 1 = 15 изменений, чтобы изменить hw ru мой дорогой ангел на оранжевый.

И вот трансформация hw ru мой дорогой ангел, как вы.

hw ru мой дорогой ангел → как ru мой дорогой ангел (вставка символа o) как ru мой дорогой ангел → как дорогой ангел (удаление 7 символов) как дорогой ангел → как ангел (удаление 2 персонажей) как ангел → как ангел (вставка e-символа) как ангел → как ang (удаление последних двух символов) как ang → как вы (подстановка последних 3 символов)

Всего 1 + 7 + 2 + 1 + 5 = 16 правки. Итак, как вы можете видеть в терминах Левинштайна, расстояние оранжевого ближе к hw ru мой дорогой ангел ;-)

ответил(а) 2021-01-25T12:26:21+03:00 4 месяца, 4 недели назад
63

Нашел что-то.. но использует mysql http://www.artfulsoftware.com/infotree/queries.php#552 Я думаю, что это намного лучше, чем обработка его на php, поскольку мои данные хранятся в базе данных.

CREATE FUNCTION levenshtein( s1 VARCHAR(255), s2 VARCHAR(255) )
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT;
DECLARE s1_char CHAR;
-- max strlen=255
DECLARE cv0, cv1 VARBINARY(256);
SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0;
IF s1 = s2 THEN
RETURN 0;
ELSEIF s1_len = 0 THEN
RETURN s2_len;
ELSEIF s2_len = 0 THEN
RETURN s1_len;
ELSE
WHILE j <= s2_len DO
SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1;
END WHILE;
WHILE i <= s1_len DO
SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1;
WHILE j <= s2_len DO
SET c = c + 1;
IF s1_char = SUBSTRING(s2, j, 1) THEN
SET cost = 0; ELSE SET cost = 1;
END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost;
IF c > c_temp THEN SET c = c_temp; END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1;
IF c > c_temp THEN
SET c = c_temp;
END IF;
SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1;
END WHILE;
SET cv1 = cv0, i = i + 1;
END WHILE;
END IF;
RETURN c;
END;

И вспомогательная функция:


CREATE FUNCTION levenshtein_ratio( s1 VARCHAR(255), s2 VARCHAR(255) )
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE s1_len, s2_len, max_len INT;
SET s1_len = LENGTH(s1), s2_len = LENGTH(s2);
IF s1_len > s2_len THEN
SET max_len = s1_len;
ELSE
SET max_len = s2_len;
END IF;
RETURN ROUND((1 - LEVENSHTEIN(s1, s2) / max_len) * 100);
END;

но я не знаю, почему эта причина ошибки You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 5 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 5

ответил(а) 2021-01-25T12:26:21+03:00 4 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема