Метрика основана на подсчёте числа совпадающих 2-значных последовательностей, реализована в виде UDF. На листе с примерами та же метрика реализована формулами - для иллюстрации принципов расчёта. Алгоритм сравнения явно нуждается в оптимизации. Собираюсь переделать его с помощью двоичного дерева. Может кто подкинет ещё идеи получше ...
Основные свойства метрики:
Область значений: от 0 до 1. 1 - полное сходство - для строк, состоящих из одинаковых слов (в одинак. гр. форме). А также для строк, состоящих из одинакового числа одинаковых 2-значных последовательностей. 0 - отсутствие сходства - для строк, не имеющих ни одной одинаковой пары последовательных знаков.
Симметрична - не меняется от перестановки аргументов. Транзитивна для 100% сходства.
Метрика основана на подсчёте числа совпадающих 2-значных последовательностей, реализована в виде UDF. На листе с примерами та же метрика реализована формулами - для иллюстрации принципов расчёта. Алгоритм сравнения явно нуждается в оптимизации. Собираюсь переделать его с помощью двоичного дерева. Может кто подкинет ещё идеи получше ...
Основные свойства метрики:
Область значений: от 0 до 1. 1 - полное сходство - для строк, состоящих из одинаковых слов (в одинак. гр. форме). А также для строк, состоящих из одинакового числа одинаковых 2-значных последовательностей. 0 - отсутствие сходства - для строк, не имеющих ни одной одинаковой пары последовательных знаков.
Симметрична - не меняется от перестановки аргументов. Транзитивна для 100% сходства.Формуляр
Ну, вот ещё кто-то заинтересовался нечётким поиском. Посмотрите не топик интеллектуальный ВПР (сопоставление ПОХОЖИХ текстов) И вообще очень много всякого можно нарыть по фразе "нечёткий поиск". Тема интересная, алгоритмов много, но до полноценной UDF так нигде и не доведено...
Ну, вот ещё кто-то заинтересовался нечётким поиском. Посмотрите не топик интеллектуальный ВПР (сопоставление ПОХОЖИХ текстов) И вообще очень много всякого можно нарыть по фразе "нечёткий поиск". Тема интересная, алгоритмов много, но до полноценной UDF так нигде и не доведено...Alex_ST
На базе вышеприведённой метрики сделал функцию поиска ближайшей по сходству строки. Методику работы с пробелами, слегка изменил: вместо дополнения по краям строки теперь их вообще игнорируем в цикле перебора знаков (а заодно - все знаки с кодом < 65 'A'). При этом результаты расчёта через формулы перестали соответствовать UDF, поэтому, формулы убрал. Ну и оптимизировал ещё кой-чего ... Для примера взял список тем форума.
Благодаря поверхностному знакомству с материалами, любезно рекомендованными Alex_ST, могу сообщить, что предлагаемая метрика является разновидностью метода Q-грамм. Увы, для более глубокого погружения в бескрайнюю тему нечёткого сходства текстов не хватает ни ума, ни времени. Может это и дилетантство, но алгоритм работает, причём работает быстро и с приемлемым качеством. Единственным заметным минусом в плане качества является высокая чувствительность к типичным длинным суффиксам типа "-ование", но, как я понял, наворченные классические алгоритмы с этой проблемой тоже не очень справляются.
На базе вышеприведённой метрики сделал функцию поиска ближайшей по сходству строки. Методику работы с пробелами, слегка изменил: вместо дополнения по краям строки теперь их вообще игнорируем в цикле перебора знаков (а заодно - все знаки с кодом < 65 'A'). При этом результаты расчёта через формулы перестали соответствовать UDF, поэтому, формулы убрал. Ну и оптимизировал ещё кой-чего ... Для примера взял список тем форума.
Благодаря поверхностному знакомству с материалами, любезно рекомендованными Alex_ST, могу сообщить, что предлагаемая метрика является разновидностью метода Q-грамм. Увы, для более глубокого погружения в бескрайнюю тему нечёткого сходства текстов не хватает ни ума, ни времени. Может это и дилетантство, но алгоритм работает, причём работает быстро и с приемлемым качеством. Единственным заметным минусом в плане качества является высокая чувствительность к типичным длинным суффиксам типа "-ование", но, как я понял, наворченные классические алгоритмы с этой проблемой тоже не очень справляются.Формуляр
Формуляр, 1. Я бы не стал на всякий случай так рисково писАть: в одном и том же модуле у вас имеется и процедура Public Function textSimilarity и в процедуре Public Function FindBestMatchTxt переменная с именем textSimilarity… Вполне можно было и "наступить на грабли" 2. А вы не думали, не удастся ли на основе вашего алгоритма сделать аналог фильтра, но с нечётким поиском?
Формуляр, 1. Я бы не стал на всякий случай так рисково писАть: в одном и том же модуле у вас имеется и процедура Public Function textSimilarity и в процедуре Public Function FindBestMatchTxt переменная с именем textSimilarity… Вполне можно было и "наступить на грабли" 2. А вы не думали, не удастся ли на основе вашего алгоритма сделать аналог фильтра, но с нечётким поиском?Alex_ST
С уважением, Алексей MS Excel 2003 - the best!!!
Сообщение отредактировал Alex_ST - Среда, 24.08.2011, 12:50
Alex_ST, спасибо за дельное замечание. Что касается 2., то для фильтра, по-хорошему, надо из функции на лист массив как-то возвращать, а таких фокусов я пока не освоил ... А если по-простому, то достаточно в FindBestMatchTxt() в блок сравнения с лучшим результатом вставить сравнение с заданным порогом и всё, что выше - копировать в какой-нибудь новый Range. Или того проще: для всего массива сравнения считаем во вспомогательном столбце textSimilarity() с искомой строкой, а дальше фильтруем/сортируем N максимальных показателей обычными средствами - вот вам и нечёткий фильтр.
Alex_ST, спасибо за дельное замечание. Что касается 2., то для фильтра, по-хорошему, надо из функции на лист массив как-то возвращать, а таких фокусов я пока не освоил ... А если по-простому, то достаточно в FindBestMatchTxt() в блок сравнения с лучшим результатом вставить сравнение с заданным порогом и всё, что выше - копировать в какой-нибудь новый Range. Или того проще: для всего массива сравнения считаем во вспомогательном столбце textSimilarity() с искомой строкой, а дальше фильтруем/сортируем N максимальных показателей обычными средствами - вот вам и нечёткий фильтр. Формуляр
Excel 2003 EN, 2013 EN
Сообщение отредактировал Формуляр - Среда, 24.08.2011, 15:23
Вот, гляньте в аттаче какой нечёткий фильтр я слепил на досуге из разных источников. Надо будет ещё с дистанцией Ливенштейна разобраться. А FUZZYMATCH работает очень неплохо.
Но вообще для целей фильтрации любым из методов нужно поискать решение как сравнивать строки разной длины (примерный поиск достаточно короткого шаблона в длинном стринге).
Я не возьмусь Дополировать чьё-то - это пожалуйста. А я сам во всех этих мудрёных метриках и дистанциях так до конца и не разобрался.
Вот, гляньте в аттаче какой нечёткий фильтр я слепил на досуге из разных источников. Надо будет ещё с дистанцией Ливенштейна разобраться. А FUZZYMATCH работает очень неплохо.
Но вообще для целей фильтрации любым из методов нужно поискать решение как сравнивать строки разной длины (примерный поиск достаточно короткого шаблона в длинном стринге).
Я не возьмусь Дополировать чьё-то - это пожалуйста. А я сам во всех этих мудрёных метриках и дистанциях так до конца и не разобрался.Alex_ST
Надо будет ещё с дистанцией Ливенштейна разобраться.
Ливенштейн, как и прочие расстояния редактирования, в моём случае не подходят - они очень чувствительны к перестановке слов, а мне нужен поиск близких по смыслу заголовков и те же слова в ином порядке должны давать 100% сходства.
Quote
А FUZZYMATCH работает очень неплохо.
Любопытно. Посмотрим на досуге ..
Alex_ST,
Quote (Alex_ST)
Надо будет ещё с дистанцией Ливенштейна разобраться.
Ливенштейн, как и прочие расстояния редактирования, в моём случае не подходят - они очень чувствительны к перестановке слов, а мне нужен поиск близких по смыслу заголовков и те же слова в ином порядке должны давать 100% сходства.
Но вообще для целей фильтрации любым из методов нужно поискать решение как сравнивать строки разной длины (примерный поиск достаточно короткого шаблона в длинном стринге).
Это вообще-то другая задача - поиск по ключевым словам. И она подразумевает совсем иные требования к метрике, например - асимметричность. Но в качестве косвенного показателя можно использовать TextSimilarity()*len(longStr)/len(shortStr)
Quote (Alex_ST)
Надо будет ещё с дистанцией Ливенштейна разобраться.
Получить сходство из расстояния (и наоборот) довольно просто: s = 1 / (d+1)
Quote (Alex_ST)
Но вообще для целей фильтрации любым из методов нужно поискать решение как сравнивать строки разной длины (примерный поиск достаточно короткого шаблона в длинном стринге).
Это вообще-то другая задача - поиск по ключевым словам. И она подразумевает совсем иные требования к метрике, например - асимметричность. Но в качестве косвенного показателя можно использовать TextSimilarity()*len(longStr)/len(shortStr)
Quote (Alex_ST)
Надо будет ещё с дистанцией Ливенштейна разобраться.
Получить сходство из расстояния (и наоборот) довольно просто: s = 1 / (d+1)Формуляр
Хоть кто-то нашёлся, разбирающийся в лингвистическом анализе!
Эх, довести бы нечёткий фильтр до юзабилити... Но тогда надо чтобы проводился нечёткий поиск по каждому из слов фразы шаблона в словах фраз фильтруемого массива... Грубо говоря, сначала оценивается сходство первого слова шаблона с каждым из слов каждой из фраз. Потом - второго, .... В итоге получаем некую матрицу с количеством строк = кол-ву фраз фильтруемого массива а количество столбцов = кол-ву слов фразы шаблона. В каждой ячейке матрицы - "похожесть" слова шаблона на слова фразы массива. Потом по этим данным нужно как-то вычислять общую похожесть каждой из фраз массива на фразу шаблона. Да... мороки много получается. Я не осилю
Хоть кто-то нашёлся, разбирающийся в лингвистическом анализе!
Эх, довести бы нечёткий фильтр до юзабилити... Но тогда надо чтобы проводился нечёткий поиск по каждому из слов фразы шаблона в словах фраз фильтруемого массива... Грубо говоря, сначала оценивается сходство первого слова шаблона с каждым из слов каждой из фраз. Потом - второго, .... В итоге получаем некую матрицу с количеством строк = кол-ву фраз фильтруемого массива а количество столбцов = кол-ву слов фразы шаблона. В каждой ячейке матрицы - "похожесть" слова шаблона на слова фразы массива. Потом по этим данным нужно как-то вычислять общую похожесть каждой из фраз массива на фразу шаблона. Да... мороки много получается. Я не осилю Alex_ST
Alex_ST, Ну, да. Идея вцелом - неплоха. Только это явно уже не подпадает под понятие "простая метрика".
Quote (Alex_ST)
Хоть кто-то нашёлся, разбирающийся в лингвистическом анализе!
Это вы, батенька, загнули! Я в этом полный профан. Влез в эту тему только в силу практической необходимости. И углубляться пока не собираюсь. Хотя, тема любопытная ...
Alex_ST, Ну, да. Идея вцелом - неплоха. Только это явно уже не подпадает под понятие "простая метрика".
Quote (Alex_ST)
Хоть кто-то нашёлся, разбирающийся в лингвистическом анализе!
Это вы, батенька, загнули! Я в этом полный профан. Влез в эту тему только в силу практической необходимости. И углубляться пока не собираюсь. Хотя, тема любопытная ...Формуляр
Ну вот - выжал из производительности алгоритма, что мог. Привинтил сравнение по дереву двоичной сортировки. И мониторинг числа несовпадающих символов на превышение предела, выше которого текущий максимум теоретически уже недостижим. Ещё добавил ограничение min уровня сходства, что тоже может сильно ускорить процесс.
Ну вот - выжал из производительности алгоритма, что мог. Привинтил сравнение по дереву двоичной сортировки. И мониторинг числа несовпадающих символов на превышение предела, выше которого текущий максимум теоретически уже недостижим. Ещё добавил ограничение min уровня сходства, что тоже может сильно ускорить процесс.Формуляр
If "A" > Mid$(text2, i + 1, 1) Then ElseIf "A" > Mid$(text2, i, 1) Then
[/vba] т.е. первое условие вообще ни к чему не приводит
Ну, это не совсем так. Вырывание команд из контекста часто ведёт к неверным выводам ... [vba]
Код
For i = Len(text1) To 1 Step -1 If "A" > Mid$(text1, i + 1, 1) Then ElseIf "A" > Mid$(text1, i, 1) Then i = i - 1 Else sumMax = sumMax + 1 sumMatch = sumMatch + profile(i) End If Next i
[/vba] Этот цикл считает число диад, содержащих только буквенные символы. Эти 2 условия эквивалентны условию [vba]
Код
If "A" > Mid$(text1, i + 1, 1) Or "A" > Mid$(text1, i, 1) Then 'пропуск текущей диады
[/vba]
Но есть у меня подозрение, что VBA всегда вычисляет обе части OR, даже если первая даёт TRUE, поэтому решено было оптимизировать проверку в явном виде, разбив на 2 части. Первое условие необходимо для проверки небуквенного символа в конце строки, а заодно (раз уж от него не избавиться) позволяет прыгать через 2 символа, когда срабатывает 2ое условие. Другое дело, что первое условие, действительно, срабатывает довольно редко: например, когда встречаются 2 пробела подряд - и оптимальнее было бы поменять условия местами. В процедуре поиска, для которой быстродействие критичней, я так и сделал. А тут - поленился.
Цитата (Alex_ST)
[vba]
Код
If "A" > Mid$(text2, i + 1, 1) Then ElseIf "A" > Mid$(text2, i, 1) Then
[/vba] т.е. первое условие вообще ни к чему не приводит
Ну, это не совсем так. Вырывание команд из контекста часто ведёт к неверным выводам ... [vba]
Код
For i = Len(text1) To 1 Step -1 If "A" > Mid$(text1, i + 1, 1) Then ElseIf "A" > Mid$(text1, i, 1) Then i = i - 1 Else sumMax = sumMax + 1 sumMatch = sumMatch + profile(i) End If Next i
[/vba] Этот цикл считает число диад, содержащих только буквенные символы. Эти 2 условия эквивалентны условию [vba]
Код
If "A" > Mid$(text1, i + 1, 1) Or "A" > Mid$(text1, i, 1) Then 'пропуск текущей диады
[/vba]
Но есть у меня подозрение, что VBA всегда вычисляет обе части OR, даже если первая даёт TRUE, поэтому решено было оптимизировать проверку в явном виде, разбив на 2 части. Первое условие необходимо для проверки небуквенного символа в конце строки, а заодно (раз уж от него не избавиться) позволяет прыгать через 2 символа, когда срабатывает 2ое условие. Другое дело, что первое условие, действительно, срабатывает довольно редко: например, когда встречаются 2 пробела подряд - и оптимальнее было бы поменять условия местами. В процедуре поиска, для которой быстродействие критичней, я так и сделал. А тут - поленился. Формуляр
Excel 2003 EN, 2013 EN
Сообщение отредактировал Формуляр - Понедельник, 29.08.2011, 18:22
Должен предупредить, что обнаружился косяк в FindBestMatchTxt(), который пока не удаётся красиво причесать. Если искомая строка и массив сравнения оказываются на разных листах происходит сбой ф-ции Intersect. Она используется для исключения искомой ячеки, в том случае, когда она находится в массиве сравнения. В общем-то, это частный случай. Так что, заплаточное решение - закомментировать в sub FindBestMatchTxt строки:
Цитата
[vba]
Код
If Not (Intersect(lookup_value, lookup_array.Cells(txtNum + 1)) Is Nothing) Then txtNum = txtNum + 1 End If
Должен предупредить, что обнаружился косяк в FindBestMatchTxt(), который пока не удаётся красиво причесать. Если искомая строка и массив сравнения оказываются на разных листах происходит сбой ф-ции Intersect. Она используется для исключения искомой ячеки, в том случае, когда она находится в массиве сравнения. В общем-то, это частный случай. Так что, заплаточное решение - закомментировать в sub FindBestMatchTxt строки:
Цитата
[vba]
Код
If Not (Intersect(lookup_value, lookup_array.Cells(txtNum + 1)) Is Nothing) Then txtNum = txtNum + 1 End If
Формуляр, я честно признаюсь, что в используемый вами алгоритм не вникал (т.к. его описания я в примерах не нашёл, а разбираться самому по комментариям лень). Поэтому просто пытаюсь "привинтить" вашу процедуру TextSimilarity к своей имитации автофильтра с нечётким поиском. А процедура TextSimilarity, повторяю, получилась достаточно шустрая и компактная. Поэтому при обработке не слишком больших массивов (в несколько тысяч строк) её вполне можно использовать как вызываемую любой другой процедурой функцию. Что я и делаю в своём примере. Конечно, если речь идёт об обработке массивов из десятков-сотен тысяч строк, то имеет смысл "ловить блох" на таком мизерном ускорении процедуры, что дают отказ от вызовов внешних процедур, замена [vba]
Код
If … Or … Then
на
Код
If … Then ElseIf … Then
[/vba] и т.п.
А FindBestMatchTxt я даже и не смотрел... Но сразу же (опять же, глубоко не вникая) могу предложить первым же шагом уйти от перебора ячеек диапазона lookup_array к перебору массива. Такой метод ВСЕГДА даёт существенное ускорение. В общем-то я бы сделал так: 1. Диапазон анализируемых строк одной командой перекинуть в массив 2. Записи массива поочерёдно сравнивать с образцом функцией TextSimilarity. 3. Каждый лучший чем предыдущий по проценту совпадения результат записывать в пару переменных "процент" и "строка". По окончании анализа массива в переменной "строка" будет текст наиболее похожей на образец строки.
Да и вообще: перед анализом хорошо бы провести "чистку" массива - убрать лишние пробелы и, наверное, знаки препинания
Формуляр, я честно признаюсь, что в используемый вами алгоритм не вникал (т.к. его описания я в примерах не нашёл, а разбираться самому по комментариям лень). Поэтому просто пытаюсь "привинтить" вашу процедуру TextSimilarity к своей имитации автофильтра с нечётким поиском. А процедура TextSimilarity, повторяю, получилась достаточно шустрая и компактная. Поэтому при обработке не слишком больших массивов (в несколько тысяч строк) её вполне можно использовать как вызываемую любой другой процедурой функцию. Что я и делаю в своём примере. Конечно, если речь идёт об обработке массивов из десятков-сотен тысяч строк, то имеет смысл "ловить блох" на таком мизерном ускорении процедуры, что дают отказ от вызовов внешних процедур, замена [vba]
Код
If … Or … Then
на
Код
If … Then ElseIf … Then
[/vba] и т.п.
А FindBestMatchTxt я даже и не смотрел... Но сразу же (опять же, глубоко не вникая) могу предложить первым же шагом уйти от перебора ячеек диапазона lookup_array к перебору массива. Такой метод ВСЕГДА даёт существенное ускорение. В общем-то я бы сделал так: 1. Диапазон анализируемых строк одной командой перекинуть в массив 2. Записи массива поочерёдно сравнивать с образцом функцией TextSimilarity. 3. Каждый лучший чем предыдущий по проценту совпадения результат записывать в пару переменных "процент" и "строка". По окончании анализа массива в переменной "строка" будет текст наиболее похожей на образец строки.
Да и вообще: перед анализом хорошо бы провести "чистку" массива - убрать лишние пробелы и, наверное, знаки препинанияAlex_ST
С уважением, Алексей MS Excel 2003 - the best!!!
Сообщение отредактировал Alex_ST - Вторник, 30.08.2011, 09:33
Конечно, если речь идёт об обработке массивов из десятков-сотен тысяч строк, то имеет смысл "ловить блох" на таком мизерном ускорении процедуры
не стану спорить, эфффект - мизерный. Это так - от тяги к перфекционизму.
Quote (Alex_ST)
Да и вообще: перед анализом хорошо бы провести "чистку" массива - убрать лишние пробелы и, наверное, знаки препинания
Боюсь чистка массива займёт не меньше времени, чем само сравнение. FindBestMatchTxt обращается к каждой ячейке диапазона только 1 раз и грузит её текст в промежуточную строку txt. Так что, не уверен. что предварительная загрузка всех ячеек в массив типа Array (если я правильно понял эту мысль) даст заметный эффект.
Quote (Alex_ST)
2. Записи массива поочерёдно сравнивать с образцом функцией TextSimilarity.
В FindBestMatchTxt происходит более сложный предварительный анализ искомого текста, что сильно ускоряет последующие циклы сравнения, и делается это 1 раз. А TextSimilarity будет свою простенькую подготовку выполнять многократно и само сравнение будет выполнять медленнее.
Quote (Alex_ST)
3. Каждый лучший чем предыдущий по проценту совпадения результат записывать в пару переменных "процент" и "строка". По окончании анализа массива в переменной "строка" будет текст наиболее похожей на образец строки.
Так оно и происходит: best - это "процент", вместо "строки" - её порядковый номер txtNum запоминается прямо в возвращаемой переменной.
Было бы очень любопытно, если бы вы провели независимое сравнение производительности поиска через TextSimilarity и через FindBestMatchTxt. А то наворотов я там оптимизирующих навешал, а какой от них реальный эффект - толком неизвестно.
Alex_ST,
Quote (Alex_ST)
Конечно, если речь идёт об обработке массивов из десятков-сотен тысяч строк, то имеет смысл "ловить блох" на таком мизерном ускорении процедуры
не стану спорить, эфффект - мизерный. Это так - от тяги к перфекционизму.
Quote (Alex_ST)
Да и вообще: перед анализом хорошо бы провести "чистку" массива - убрать лишние пробелы и, наверное, знаки препинания
Боюсь чистка массива займёт не меньше времени, чем само сравнение. FindBestMatchTxt обращается к каждой ячейке диапазона только 1 раз и грузит её текст в промежуточную строку txt. Так что, не уверен. что предварительная загрузка всех ячеек в массив типа Array (если я правильно понял эту мысль) даст заметный эффект.
Quote (Alex_ST)
2. Записи массива поочерёдно сравнивать с образцом функцией TextSimilarity.
В FindBestMatchTxt происходит более сложный предварительный анализ искомого текста, что сильно ускоряет последующие циклы сравнения, и делается это 1 раз. А TextSimilarity будет свою простенькую подготовку выполнять многократно и само сравнение будет выполнять медленнее.
Quote (Alex_ST)
3. Каждый лучший чем предыдущий по проценту совпадения результат записывать в пару переменных "процент" и "строка". По окончании анализа массива в переменной "строка" будет текст наиболее похожей на образец строки.
Так оно и происходит: best - это "процент", вместо "строки" - её порядковый номер txtNum запоминается прямо в возвращаемой переменной.
Было бы очень любопытно, если бы вы провели независимое сравнение производительности поиска через TextSimilarity и через FindBestMatchTxt. А то наворотов я там оптимизирующих навешал, а какой от них реальный эффект - толком неизвестно.Формуляр
Excel 2003 EN, 2013 EN
Сообщение отредактировал Формуляр - Вторник, 30.08.2011, 10:58
Формуляр, я не хочу использовать FindBestMatchTxt потому, что она возвращает только одно значение, которое ПО ЕЁ МНЕНИЮ мне больше всего подходит. А я хочу доверить функции нечёткого поиска только приблизительную предварительную фильтрацию записей чтобы из результатов иметь возможность уже САМОМУ ВЫБРАТЬ наиболее подходящий мне вариант. Чистку массива от лишних пробелов (лидирующих, финиширующих, внутренних) можно, ИМХО, сделать всего один раз и даже прямо на листе "одним махом" применив Application.WorksheetFunction.Trim сразу ко всем ячейкам фильтруемого диапазона (нечего грязь на листе хранить!). Для удаления знаков пунктуации из записей массива можно применить регулярные выражения (RegExp). Тоже только один раз. И это тоже недолго. А работа с массивом ВСЕГДА даёт выигрыш в скорости чуть ли не на порядок по сравнению с работой непосредственно с ячейками. Поэтому если записей в фильтруемом диапазоне не сотни, а десятки тысяч, то выигрыш будет значительным.
Формуляр, я не хочу использовать FindBestMatchTxt потому, что она возвращает только одно значение, которое ПО ЕЁ МНЕНИЮ мне больше всего подходит. А я хочу доверить функции нечёткого поиска только приблизительную предварительную фильтрацию записей чтобы из результатов иметь возможность уже САМОМУ ВЫБРАТЬ наиболее подходящий мне вариант. Чистку массива от лишних пробелов (лидирующих, финиширующих, внутренних) можно, ИМХО, сделать всего один раз и даже прямо на листе "одним махом" применив Application.WorksheetFunction.Trim сразу ко всем ячейкам фильтруемого диапазона (нечего грязь на листе хранить!). Для удаления знаков пунктуации из записей массива можно применить регулярные выражения (RegExp). Тоже только один раз. И это тоже недолго. А работа с массивом ВСЕГДА даёт выигрыш в скорости чуть ли не на порядок по сравнению с работой непосредственно с ячейками. Поэтому если записей в фильтруемом диапазоне не сотни, а десятки тысяч, то выигрыш будет значительным.Alex_ST
Вот. Максимально причесал. Добавил чистку от пробелов и пунктуации с помощью RegExp. Требуемую степень сходства теперь можно удобно регулировать движком полоски прокрутки (но можно и в ручную). Пока другие методы удалять не стал на всякий случай.
Вот. Максимально причесал. Добавил чистку от пробелов и пунктуации с помощью RegExp. Требуемую степень сходства теперь можно удобно регулировать движком полоски прокрутки (но можно и в ручную). Пока другие методы удалять не стал на всякий случай.Alex_ST