Це один із дослідницьких проектів, які я зараз переживаю. Вимога майже точно така, як ваша, і ми розробили приємні алгоритми для вирішення проблеми.
Вхідні дані
Вхід - це нескінченний потік англійських слів або фраз (ми їх називаємо tokens
).
Вихід
- Вивести топ N лексем, які ми бачили досі (з усіх жетонів, які ми бачили!)
- Виведіть вершину N лексем у історичному вікні, скажімо, за останній день чи минулий тиждень.
Застосування цього дослідження полягає у пошуку гарячої теми чи тенденцій теми у Twitter або Facebook. У нас є сканер, який сканує на веб-сайті, який генерує потік слів, які надходять у систему. Потім система виводить слова або фрази з найвищою частотою або в цілому, або в історичному періоді. Уявіть, що за останні кілька тижнів фраза "Кубок світу" з’явиться багато разів у Twitter. Так само робить "Павло восьминога". :)
Рядок в цілі
Система має ціле ідентифікатор для кожного слова. Хоча в Інтернеті майже нескінченно можливих слів, але після накопичення великого набору слів можливість пошуку нових слів стає все нижчою і нижчою. Ми вже знайшли 4 мільйони різних слів і призначили для кожного унікальний ідентифікатор. Весь цей набір даних може бути завантажений у пам'ять як хеш-таблиця, витрачаючи приблизно 300 МБ пам'яті. (Ми реалізували власну хеш-таблицю. Реалізація Java займає величезні витрати на пам'ять)
Кожну фразу тоді можна ідентифікувати як масив цілих чисел.
Це важливо, тому що сортування та порівняння за цілими числами набагато швидше, ніж на рядках.
Архівні дані
Система зберігає архівні дані для кожного маркера. В основному це пари (Token, Frequency)
. Однак таблиця, в якій зберігаються дані, була б такою величезною, що нам доведеться розділити таблицю фізично. Після того, як схема розділів заснована на nграм токена. Якщо маркер - це одне слово, це 1грам. Якщо маркер - це двословна фраза, це 2грам. І це продовжується. Приблизно в 4грам ми маємо 1 мільярд записів, розмір таблиці - близько 60 ГБ.
Обробка вхідних потоків
Система поглинає вхідні пропозиції, поки пам'ять не буде повністю використана (так, нам потрібен MemoryManager). Після прийняття N пропозицій і зберігання в пам'яті система робить паузу і починає токенізувати кожне речення словами та фразами. Кожен маркер (слово чи фраза) рахується.
Для високочастотних жетонів вони завжди зберігаються в пам'яті. Для менш часто зустрічаються лексем вони сортуються на основі ідентифікаторів (пам'ятаємо, що ми переводимо String в масив цілих чисел) і серіалізуємо в файл диска.
(Однак, для вашої проблеми, оскільки ви рахуєте лише слова, ви можете помістити всю карту частот слова лише в пам'ять. Ретельно розроблена структура даних споживатиме лише 300 МБ пам'яті на 4 мільйони різних слів. Деякі підказки: використовуйте діаграму ASCII для представляють рядки), і це набагато прийнятно.
Тим часом буде ще один процес, який активується, як тільки він знайде будь-який диск-файл, згенерований системою, а потім почніть його об’єднання. Оскільки файл диска відсортовано, об'єднання зайняло б подібний процес, як сортування злиття. Деякі дизайни повинні бути обережні і тут, оскільки ми хочемо уникати занадто багато випадкових дискових пошуків. Ідея полягає у тому, щоб уникнути одночасного читання (процес злиття) / запису (вихід системи), і нехай процес злиття читає форму одного диска під час запису на інший диск. Це схоже на реалізацію блокування.
Кінець дня
Наприкінці дня система матиме багато частих жетонів з частотою, що зберігаються в пам'яті, та багато інших менш частих жетонів, що зберігаються в декількох дискових файлах (і кожен файл сортується).
Система передає карту пам'яті в файл диска (сортуйте її). Тепер проблемою стає злиття набору відсортованого файлу диска. Використовуючи подібний процес, ми отримаємо один відсортований файл диска наприкінці.
Потім, остаточне завдання - об'єднати відсортований файл диска в архівну базу даних. Залежить від розміру архівної бази даних, алгоритм працює як нижче, якщо він досить великий:
for each record in sorted disk file
update archive database by increasing frequency
if rowcount == 0 then put the record into a list
end for
for each record in the list of having rowcount == 0
insert into archive database
end for
Інтуїція полягає в тому, що через деякий час кількість вставок стане все меншою і меншою. Все більше операцій буде стосуватися лише оновлення. І це оновлення не покарається за індексом.
Сподіваюся, що ціле пояснення допоможе. :)
what is the most frequent item in the subsequence [2; 2; 3; 3; 3; 4; 4; 4; 4; 5; 5] of your sequence?