Пошук елемента, який найбільше трапляється у дуже великому файлі


12

Я чув, що це запитання про інтерв'ю задавали багато питань, і я сподівався отримати деякі думки щодо того, які хороші відповіді можуть бути: у вас великий файл 10+ Гб і ви хочете дізнатися, який елемент найбільше трапляється, який хороший спосіб зробити це?

Ітерація та відстеження на карті, мабуть, не є хорошою ідеєю, оскільки ви використовуєте багато пам’яті, а відстеження, коли записи надходять, не є найкращим варіантом, оскільки, коли це питання задається, файл, як правило, вже існує.

Інші думки я включив розділення файлу, який слід повторити і обробити декількома потоками, а потім об'єднати ці результати, але проблема пам'яті для карт все ще існує.


2
Які елементи файлу? Вони струнні? Якщо взяти символи для елементів, карта не матиме проблеми з пам'яттю. Якщо елементи - це слова, то я знову думаю, що це не буде проблемою. Якщо у вас є всі можливі підрядки, то у вас можуть виникнути проблеми ...
Нейк

1
Якщо умовою був "елемент, який виявляється більше половини загальних елементів", тоді було лінійне рішення.
st0le

Я вважаю, що елементи зазвичай - це рядки. Але я не бачу, як карта - це не проблема. У гіршому випадку, коли кожен елемент унікальний, ви не просто подвоїли вимогу пам'яті?
Пт

1
Якщо алгоритм кандидата більшості Боєра-Мура застосований, він працює в лінійний час і не працює.
Джухо

Відповіді:


6

Коли у вас дійсно великий файл і багато елементів у ньому, але найпоширеніший елемент дуже поширений - трапляється частина часу - ви можете його знайти в лінійному часі з просторовими словами ( константа в нотації дуже мала, в основному 2, якщо ви не рахуєте сховища для допоміжних речей, таких як хешування). Більше того, це чудово спрацьовує із зовнішнім сховищем, оскільки файл обробляється послідовно один за одним, а алгоритм ніколи не «озирається назад». Один із способів зробити це - за допомогою класичного алгоритму Місри та Гріса, див. Ці конспекти лекцій . Зараз ця проблема відома як проблема важких нападників (частими елементами є важкі нападники).O ( k ) O ( )>1/kO(k)O()

Припущення, що найчастішим елементом виявляється частина часу для невеликої кількості, може здатися сильним, але це певним чином необхідно! Тобто, якщо у вас буде послідовний доступ до вашого файлу (і якщо файл величезний випадковий доступ буде занадто дорогим), будь-який алгоритм, який завжди знаходить найчастіший елемент у постійній кількості проходів, буде використовувати лінійний простір у кількості елементів . Отже, якщо ви щось не припускаєте про вхід, ви не можете перемогти хеш-таблицю. Припущення про те, що найчастіший елемент дуже часто є, можливо, найбільш природним способом подолати негативні результати.к>1/kk

Ось ескіз для , тобто коли є один елемент, який зустрічається більше половини часу. Цей особливий випадок відомий як алгоритм більшості голосів і пояснюється Боєром та Муром. Ми збережемо один елемент і один рахунок. Ініціалізуйте кількість до 1 і збережіть перший елемент файлу. Потім обробіть файл послідовно:k=2

  • якщо поточний елемент файлу такий же, як збережений елемент, збільште кількість на одиницю
  • якщо поточний елемент файлу відрізняється від збереженого елемента, зменшіть кількість на одиницю
  • якщо оновлений кількість дорівнює 0, "вимкніть" збережений елемент і збережіть поточний елемент файлу; збільшити кількість до 1
  • переходимо до наступного елемента файлу

Трохи задумавшись над цією процедурою, переконайте вас, що якщо існує елемент "більшості", тобто такий, який відбувається більше половини часу, то цей елемент буде збереженим елементом після обробки всього файлу.

Загальних , ви тримаєте елементи і розраховує, і ініціалізувати елементи до першого різних елементів файлу і розраховує на кількість разів , кожен з цих елементів з'являється , перш ніж ви бачите -й виразний елемент. Тоді ви запускаєте по суті ту саму процедуру: кількість елементів збільшується щоразу, коли виникає, кількість елементів зменшується, якщо зустрічається елемент, який не зберігається, а коли деякий підрахунок дорівнює нулю, цей елемент витісняється на користь поточний елемент файлу. Це алгоритм Місра-Гріса.k - 1 k - 1 k kkk1k1kk

Звичайно, ви можете використовувати хеш-таблицю для індексації збережених елементів . Після припинення цей алгоритм гарантовано повертає будь-який елемент, який зустрічається більше частки часу. Це, по суті, найкраще, що ви можете зробити з алгоритмом, який робить постійну кількість проходів по файлу і зберігає лише слова.1 / k O ( k )k11/kO(k)

k1/kk1


Не можна використовувати алгоритми Бойєра-Мура або Місра-Гріса-Демена. Як зазначено, проблема полягає в іншому: ви шукаєте не більшість елементів, а елемент, в якому виникають> = виникнення всіх елементів. Ось простий контрприклад. Нехай n - загальна кількість елементів, така що n = 2k + 1 . Нехай перший k елементів дорівнює 0, наступний k елементів - 1, а останній - 2. Алгоритм Бойєра-Мура повідомить про останній елемент 2 як потенційний кандидат більшості. Але для цього конкретного випадку вихід повинен бути або 0, або 1.
Массімо Кафаро

O(1)Ω(n)

Я щойно зазначив, що якщо ви зробите неправильне припущення, ви можете отримати неправильні результати. Що краще, невеликий слід пам’яті та потенційно неправильний результат чи правильний результат, хоча це коштує вам ще трохи пам’яті? Якби мені довелося вибрати потенційно невірний результат, я б пішов на рандомізований алгоритм, а не на Бойєра-Мура, припускаючи, що я не знаю, що це насправді правда.
Массімо Кафаро

@MassimoCafaro - це не компроміс, який потрібно приймати. як я зазначив, один пропуск по файлу легко перевіряє, чи задоволення припущено!
Сашо Ніколов

@MassimoCafaro, і це лише тривіальне рішення! припущення можна з високою ймовірністю перевірити за допомогою ескізу CM без додаткових пропусків.
Сашо Ніколов

3

Очевидна відповідь, звичайно, зберігати хеш-карту і зберігати лічильник появи елементів під час переміщення по файлу, як уже запропонував Nejc. Це (за часовою складністю) оптимальне рішення.

Θ(nlogn).


Чи можете ви детальніше розглянути детальніше про підхід Хаффмана до кодування? Я раніше писав кодер Хаффмана, але це було деякий час, як саме ви його використовуєте в цьому випадку?
Пат

@Pat Не забудьте про цю частину рано вранці, і я якось подумав, що має сенс стиснути вхід.
Jernej

1

Якщо найпоширеніший елемент є більш поширеним, ніж наступний загальний елемент за значним запасом, а кількість різних елементів невелика порівняно з розміром файлу, ви можете випадковим чином відібрати пару елементів і повернути найпоширеніший елемент у вашій вибірці.


Більше того, якщо є мала кількість елементів, що трапляються багато разів, ви можете їх знайти шляхом вибірки, а потім точно порахувати лише ці елементи.
Макс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.