Как посчитать количество уникальных слов в тексте
Перейти к содержимому

Как посчитать количество уникальных слов в тексте

  • автор:

Как посчитать количество уникальных слов в тексте

Hellp. не могу решить задачу

Подсчет числа уникальных слов

Описание задачи
Создать обработку, подсчитывающую число уникальных слов во введенном пользователем тексте.

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

Процесс выполнения
Создайте внешнюю обработку с именем, например, ПодсчетЧислаУникальныхСлов.
Добавьте в нее реквизит типа Строка — например, Текст — и перетащите его на форму, сделав многострочным полем ввода.
Добавьте команду Подсчитать и перетащите ее кнопкой на форму.
В обработчике команды:
Создайте Соответствие для хранения уникальных слов.
Разделите текст на слова вызовом СтрРазделить().
Обойдите в цикле все слова.
Вставляйте в соответствие слово, приведя его к верхнему или нижнему регистру.
Выведите результат — число элементов соответствия — вызовом Сообщить() или ПоказатьПредупреждение(), формируя строку с помощью СтрШаблон().

(0) в твоем посте все есть.
(0) Во-первых, тема не отражает суть сообщения;
Во-вторых, не сдашь ты свой зачёт

Две строчки кода и запрос
Разобрать строку в массив подстрок, только не в массив а в ТЗ и запрос к ней с Имеющие
все

Как посчитать количество уникальных слов?

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

Отслеживать
задан 21 дек 2020 в 15:01
33 4 4 бронзовых знака
Нельзя сравнивать явно? Используйте set .
21 дек 2020 в 15:03

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

21 дек 2020 в 15:08
@AndrejLevkovitch Никакой реальный хеш не сможет гарантировать отсутствие коллизий.
21 дек 2020 в 15:10

@Harry никто и не спорит, но, думаю, что раз речь идет о тексте (а в языках ограниченное и относительно небольшое количество слов), то это не будет проблемой.

21 дек 2020 в 15:14

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Написал небольшую программу, которая выводит число уникальных слов и сами слова. Использует std::unordered_set который не сравнивает слова на равенство, а определяет уникальность вычислением хеш функции от слов, плюс изредка сравнивает на равенство при совпадении хеша для перестраховки.

Такой алгоритм должен работать быстро для больших данных и его время выполнения растёт линейно с ростом размера текста.

Для разбиения текста на слова используется модуль std::regex, т.е. регулярные выражения, регулярное выражение у меня определяет разделитель слов, другими словами знаки пунктуации и пробелы, отредактируйте sepre выражение чтобы добавить новые знаки разделения.

#include #include #include #include int main() < std::string text = "Hello, world! world again."; std::unordered_setwords; std::regex sepre(R"([\s,\.\!\-]+)"); std::sregex_token_iterator iter(text.begin(), text.end(), sepre, -1), end; for (; iter != end; ++iter) words.insert(*iter); std::cout
Number of unique words: 3 again|Hello|world| 

Предыдущий вариант решения отличается тем, что он всё таки делает сравнение строк на равенство, но редко. Т.е. если вопрос в скорости то он идеальный алгоритм. Но если задача стоит именно никогда не сравнивать строки, то предлагаю второй вариант. Он использует только равенство хешей для определения совпадения строк, т.е. строки никогда не сравнивает. Для этого я использовал std::unordered_map и std::hash. Единственный недостаток этого алгоритма в том что очень-очень редко при коллизии хешей (совпадении хешей) он может пропустить некоторые слова, но эту ошибку он допускает супер редко (раз на триллион слов на 64-битных машинах, при условии что компилятор реализовал качественную хеш функцию), так что можно считать что алгоритм идеальный в практических условиях.

#include #include #include #include #include int main() < std::string text = "Hello, world! world again."; std::unordered_mapwords; std::hash hasher; std::regex sepre(R"([\s,\.\!\-]+)"); std::sregex_token_iterator iter(text.begin(), text.end(), sepre, -1), end; for (; iter != end; ++iter) words[hasher(*iter)] = *iter; std::cout

Также в последнем алгоритме выше можно заметить, что используется лишняя операция и память для хранения самих слов. Если нам нужно вывести только число уникальных слов, но не нужно выводить сами уникальные слова, то алгоритм можно упростить и ускорить и сэкономить память, убрав хранение самих слов и используя std::unordered_set. Также в этом финальном алгоритме можно заметить, что раз мы не храним слов вообще, то очевидно что мы их не сравниваем и даже не можем сравнить на равенство никак.

#include #include #include #include #include int main() < std::string text = "Hello, world! world again."; std::unordered_setwords; std::hash hasher; std::regex sepre(R"([\s,\.\!\-]+)"); std::sregex_token_iterator iter(text.begin(), text.end(), sepre, -1), end; for (; iter != end; ++iter) words.insert(hasher(*iter)); std::cout

Замечание. Я в коде использовал стандартные хеш-функции предоставленные в STL компилятором. На самом деле компилятор может их реализовать как стойкими к коллизиям, так и нет, стандартом это не оговаривается. От этого будет зависеть качество алгоритма. Если необходимо очень точное решение, то нужно взять SHA-512 функцию в интернете (есть много авторов GitHub кто предоставляет компактную реализацию этой функции в одно-файловой библиотеке). Если стоит вопрос скорости, то лучше взять xxhash, он необычайно быстрый и при этом имеет очень мало коллизий.

Выделение уникальных слов в тексте

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

Наберите или скопируйте текст для анализа:

// Эта функция выделяет из текста в $text все уникальные слова и // возвращает их список. В необязательный параметр $nOrigWords // помещается исходное число слов в тексте, которое было до // "фильтрации" дубликатов. function getUniques($text, &$nOrigWords=false) < // Сначала получаем все слова в тексте. $words=preg_split('/[^a-zA-Zа-яА-ЯёЁ0-9]+/u', $text, -1, PREG_SPLIT_NO_EMPTY); $nOrigWords = count($words); // Затем приводим слова к нижнему регистру. $words = array_map("mb_strtolower", $words); // Получаем уникальные значения. $words = array_unique($words); return $words; >// Пример применения функции. setlocale(LC_ALL, ''); $fname = "largetextfile.txt"; $text = file_get_contents($fname); $uniq = getUniques($text, $nOrig); echo "Было слов: $nOrig
"; echo "Стало слов: ".count($uniq)."
"; echo join(" ", $uniq);

Также Вам может быть интересен сервис Семантический анализатор, который позволяет посчитать все слова на страице с учетом морфологии.

Как посчитать количество уникальных слов в тексте

Изменено: Jack Famous - 10.10.2019 14:46:41 ( Увеличил список символов и убрал чувствительность к регистру (количество уникальных упало с 290 до 267 = 23 слова) )

Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄

Пользователь
Сообщений: 7 Регистрация: 10.10.2019
10.10.2019 14:56:43

БМВ,именно так! Действительно, нужен макрос. Слова через дефис считаются как одно.

Jack Famous, давайте я лучше приложу весь документ. Там более 30 000 ячеек. Я точно накосячу, если буду писать формулу самостоятельно. Excel последний раз в школьной программе встречался.

Пользователь
Сообщений: 11886 Регистрация: 07.11.2014
OS: Win 10 Корп. x64 | Excel 2016 x64: | Browser: Chrome
10.10.2019 15:03:01

Varjo, я в вас верю - удачи

P.S.: сейчас дефисы удаляются…

Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄

Пользователь
Сообщений: 7 Регистрация: 10.10.2019
10.10.2019 16:08:37

Jack Famous, все ячейки разом посчитать не даёт. Считает чуть больше тысячи. Подскажите, есть ли возможность посчитать всё за один раз?

Пользователь
Сообщений: 11886 Регистрация: 07.11.2014
OS: Win 10 Корп. x64 | Excel 2016 x64: | Browser: Chrome
10.10.2019 16:28:54
Varjo, всё считает без ограничений. Выделите столбец с данными целиком и запустите макрос

Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄

Пользователь
Сообщений: 7 Регистрация: 10.10.2019
10.10.2019 16:38:16
Jack Famous, так и делаю. Пишет "run-time error 13: type mismatch".
Пользователь
Сообщений: 11886 Регистрация: 07.11.2014
OS: Win 10 Корп. x64 | Excel 2016 x64: | Browser: Chrome
10.10.2019 16:41:17

Varjo,
1. в моём файле делаете?
2. нажмите Debug и покажите скрин строки, на которой возникла ошибка (подсвечена жёлтым)

Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄

Пользователь
Сообщений: 7 Регистрация: 10.10.2019
10.10.2019 16:50:19

Jack Famous, в вашем файле. Вот скриншот.

Пользователь
Сообщений: 11886 Регистрация: 07.11.2014
OS: Win 10 Корп. x64 | Excel 2016 x64: | Browser: Chrome
10.10.2019 17:10:00
Varjo, у вас ошибки в строках 1365 и 1366. Добавил проверку. Результат 13 112 на полных данных
Для первого столбца (НЕ для выделения)

Option Explicit '=========================================================================================== Sub CountUniq() Dim dic As New Dictionary Dim x, w, arr 'arr = Intersect(Selection, ActiveSheet.UsedRange).Value2 ' для выделенного диапазона arr = Intersect(Columns(1), ActiveSheet.UsedRange).Value2 ' для ПЕРВОГО столбца If Not IsArray(arr) Then arr = Array(arr) For Each x In arr If Not IsError(x) Then If Len(x) Then Replacer x x = Split(x) For Each w In x: w = dic(w): Next w End If End If Next x MsgBox Format$(dic.Count, "# ### ### ### elements"), vbInformation, "DONE" End Sub '=========================================================================================== Private Sub Replacer(iVal) Dim sym, arrDel() arrDel = Array(".", ",", "-", "", "/", "\", "?", ":", "…", "", "") ' перечень удаляемых символов iVal = WorksheetFunction.Clean(WorksheetFunction.Trim(Replace$(iVal, Chr(160), Chr(32)))) If Len(iVal) Then For Each sym In arrDel If InStr(iVal, sym) Then iVal = Replace$(iVal, sym, "") If Len(iVal) = 0 Then Exit For Next sym iVal = UCase$(iVal) End If End Sub '===========================================================================================

UPD: это игровые диалоги что-ли или манга какая?
Вам нужно менять подход (и макрос) - у вас куча символов типа "[]()%^", которые надо удалять, проблема со словами через дефис, числа (что с ними делать), числа через дефис и т.д. Скорее всего в платную ветку…

Изменено: Jack Famous - 10.10.2019 17:16:06

Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *