Когда у вас есть действительно большой файл и много элементов в нем, но самый распространенный элемент очень распространен - встречается доли времени - вы можете найти его в линейном времени с пробелом слов ( Константа в нотации очень мала, в основном 2, если вы не учитываете память для вспомогательных вещей, таких как хеширование). Более того, это прекрасно работает с внешним хранилищем, поскольку файл обрабатывается последовательно по одному элементу за раз, и алгоритм никогда не «оглядывается назад». Один из способов сделать это - использовать классический алгоритм Мисры и Гриса, см. Эти лекционные заметки . Проблема теперь известна как проблема тяжелых нападающих (частые элементы - тяжелые нападающие).O ( k ) O ( )>1/kO(k)O()
Предположение, что наиболее частый элемент появляется доли времени для небольшого числа, может показаться сильным, но это необходимо! Т.е., если у вас будет последовательный доступ к вашему файлу (и в случае, если файл огромен, произвольный доступ будет слишком дорогим), любой алгоритм, который всегда находит наиболее частый элемент за постоянное число проходов, будет использовать пространство, линейное по количеству элементов. , Поэтому, если вы не предполагаете что-то о вводе, вы не можете разбить хеш-таблицу. Предположение, что наиболее частый элемент является очень частым, возможно, является наиболее естественным способом обойти отрицательные результаты.к>1/kk
Вот набросок для , то есть когда есть один элемент, который встречается более половины времени. Этот особый случай известен как алгоритм большинства голосов и связан с Бойером и Муром. Мы будем хранить один элемент и один счет. Инициализируйте счетчик до 1 и сохраните первый элемент файла. Затем обработайте файл в последовательности:k=2
- если текущий элемент файла совпадает с хранимым элементом, увеличьте число на единицу
- если текущий элемент файла отличается от хранимого элемента, уменьшите счет на единицу
- если обновленный счетчик равен 0, «выкинуть» сохраненный элемент и сохранить текущий элемент файла; увеличить счет до 1
- перейти к следующему элементу файла
Немного подумав об этой процедуре, вы убедите вас в том, что если существует элемент «большинства», то есть тот, который встречается более половины времени, то этот элемент будет сохраненным элементом после обработки всего файла.
Общих , вы держите элементы и рассчитывает, и инициализировать элементы к первому различных элементам файла и рассчитывает на количество раз , каждый из этих элементов появляется , прежде чем вы видите -й отличный элемент. Затем вы запускаете, по сути, ту же процедуру: счетчик элемента увеличивается каждый раз, когда он встречается, все счетчики элементов уменьшаются, если встречается элемент, который не сохранен, и когда некоторое количество равно нулю, этот элемент исключается в пользу текущий элемент файла. Это алгоритм Мисры-Гриса.k - 1 k - 1 k kkk−1k−1kk
Конечно, вы можете использовать хеш-таблицу для индексации хранимых элементов. При завершении этот алгоритм гарантированно возвращает любой элемент, который встречается более чем в доле времени. По сути, это лучшее, что вы можете сделать с алгоритмом, который делает постоянное количество проходов по файлу и сохраняет только слов.1 / k O ( k )k−11/kO(k)
И последнее: после того, как вы нашли кандидатов в «сильные нападающие» (то есть частые элементы), вы можете сделать еще один проход по файлу для подсчета частоты каждого элемента. Таким образом, вы можете ранжировать элементы между собой и проверить, все ли они встречаются более чем в доле времени (если таких элементов меньше, чем , некоторые из элементов, возвращаемых алгоритмом, могут быть ложноположительными ).1kk - 11/kk−1