Різниця обчислювальної множини між двома великими множинами


14

У мене є два великих наборів цілих чисел A і B . Кожен набір містить близько мільйона записів, і кожен запис - це натуральне число, яке становить не більше 10 цифр.

Який найкращий алгоритм для обчислення AB і BA ? Іншими словами, як я можу ефективно обчислити список записів A , які не є у B і навпаки? Яка найкраща структура даних для представлення цих двох наборів, щоб зробити ці операції ефективними?

Найкращий підхід, який я можу придумати, - це зберігання цих двох наборів у вигляді відсортованих списків та порівняння кожного елемента A проти кожного елемента B , лінійним способом. Чи можемо ми зробити краще?


Якщо ви готові зберігати його по-різному, можливо, ви зможете отримати кращі результати.
Realz Slaw

Крім того, якщо ви готові отримати результати у вигляді неявної структури даних; ви можете просто створити таку структуру, яка запитує два набори, щоб відповісти на кожен власний запит.
Realz Slaw

1
@ user917279 Одним із важливих моментів є: зазвичай ви можете компромісувати попередню обробку / час побудови, час запиту та використання пам'яті один проти одного. Ви редагуєте structe рідко, але запитуєте багато? Навпаки? Пам'ять хвилює чи ні? На такі запитання можна відповісти з практичної точки зору та інформувати про вибір "правильного" "теоретичного" конструкту.
Рафаель

1
@Raphael Ви вважаєте, що можна зробити краще, ніж незрозумілі стійкі набори (за складністю), використовуючи більше пам’яті та / або витрачаючи більше часу на підготовку. Мені просто цікаво, якщо ви думаєте, що це можливо. Я не бачу таблиці пошуку як варіант для наборів вводу такого розміру.
smossen

1
@ user917279 Якщо ви розглядаєте приклад двох величезних наборів, які однакові, то будь-яка структура даних, створена за допомогою хеш-консистенції, підтримує тестування рівності в O (1), оскільки однакові структури будуть об'єднані при створенні і таким чином матимуть однакове місце пам'яті. Збіжно стійкі набори використовують переваги хеш-консистенції також тоді, коли дві структури майже рівні. Складність - найкраща, яку я бачив поки що для замовлених наборів.
smossen

Відповіді:


9

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

Нехай I=O(min(|A|,|B|,|AΔB|))

Тоді ви можете виконати операції і A Δ B , кожна з яких O ( I log | A | + | B |AB,AB,ABAΔBочікуваний час. Отже, по суті, ви отримуєте мінімальний розмір двох наборів, або - розмір симетричної різниці, залежно від того, що менше. Це краще, ніж лінійне, якщо симетрична різниця невелика; тобто. якщо вони мають велике перехрестя. Насправді для двох операцій з різницею множин, які ви хочете, це практично чутливо до виходу, оскільки разом вони складають розмір симетричної різниці.O(Ilog|A|+|B|I)

Додаткову інформацію див. У розділі Налаштовані стійкі набори та карти від Olle Liljenzin (2013).


Обриви у статті впорядковані пошукові дерева. Я б не зараховував їх до несортивних структур даних.
smossen

@smossen досить правдивий, я це відредагував.
Realz Slaw

6

Лінійне сканування - це найкраще, що я знаю, як зробити, якщо набори представлені у вигляді відсортованих пов'язаних списків. Час роботи - .O(|A|+|B|)

Зауважте, що вам не потрібно порівнювати кожен елемент проти кожного елемента B , попарно. Це призвело б до часу виконання O ( |AB , що значно гірше. Натомість для обчислення симетричної різниці цих двох множин можна використовувати техніку, аналогічну операції "злиття" в об'єднанні, відповідним чином модифіковану для опущення значень, спільних для обох наборів.O(|A|×|B|)

Більш детально, ви можете побудувати рекурсивний алгоритм, як описано нижче для обчислення AB , припускаючи, що і B представлені як пов'язані списки зі своїми значеннями у відсортованому порядку:AB

difference(A, B):
    if len(B)=0:
        return A # return the leftover list
    if len(A)=0:
        return B # return the leftover list
    if A[0] < B[0]:
        return [A[0]] + difference(A[1:], B)
    elsif A[0] = B[0]:
        return difference(A[1:], B[1:])  # omit the common element
    else:
        return [B[0]] + difference(A, B[1:])

Я представляв це в псевдо-Python. Якщо ви не читаєте Python, він A[0]є головою пов'язаного списку A, A[1:]є рештою списку і +являє собою конкатенацію списків. З міркувань ефективності, якщо ви працюєте в Python, ви, мабуть, не хотіли б реалізувати його саме так, як вище - наприклад, може бути краще використовувати генератори, щоб уникнути створення багатьох тимчасових списків - але я хотів покажіть ідеї у найпростішій можливій формі. Мета цього псевдокоду - просто проілюструвати алгоритм, а не запропонувати конкретну реалізацію.

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


фантастично, чи є у нас інші варіанти, якщо обмеження, що набори потрібно зберігати як упорядковані списки, буде знято?
user917279

2

Якщо A і B мають однаковий розмір, роз'єднані та переплетені (наприклад, непарні числа в A і парні числа в B), то порівняння парних елементів у лінійний час, ймовірно, є оптимальним.

Якщо A і B містять блоки елементів, які знаходяться точно в одному з A або B, або в обох з них, можна обчислити різницю множин, об'єднання та перетину в сублінійний час. Наприклад, якщо A і B різняться в точно одному елементі, різницю можна обчислити в O (log n).

http://arxiv.org/abs/1301.3388


1
Він каже, що набори впорядковані, що може означати, що вони зберігаються як списки, дерева пошуку чи щось інше. Якщо дані потрібно зберігати у вигляді списків, досить нецікаво просити "найкращий алгоритм для обчислення AB", коли жоден алгоритм не міг би зробити краще, ніж сканування списків у лінійний час (для якого він уже знайшов алгоритм).
smossen

1
Боже, ви зв'язали той самий папір, що і я (я, такий же, як і ви, швидше) ... назвіть свої посилання наступного разу: D
Realz Slaw

@smossen фантастично, наскільки б я не знав (?), я представляв їх як упорядковані списки, але покірно вітаю й інші пропозиції.
user917279

2

один із варіантів полягає у використанні бітвекторів для представлення наборів (де -та позиція представляє наявність або відсутність елемента), а операції типу набору потім зводяться до двійкових операцій, які можна швидко виконувати (і на декількох бітах паралельно) на цифрових комп'ютерах . в цьому випадку A - B = a ¯ b, де a , b - бітрейктори. відносна ефективність цієї техніки порівняно з іншими методами також залежить від розрідженості. для більш щільних наборів він може бути ефективнішим, ніж інші підходи. також, звичайно, вся операція бентежно паралельна, тому встановлені операції можна робити паралельно.nABab¯a,b


1010

1
Р., пропускає пункт. одномісний longможе зберігати 32 елементи або 1 byte, 8 елементів. тож записи 1М можна зберігати лише у ~ 125 КБ оперативної пам’яті! сховище може бути значно ефективнішим, ніж інші представлення, залежно від того, як реалізується проблема ...
vzn

Тож вам знадобиться понад 12 Мб для наборів, якими цікавиться ОП. Це вибухає всі кеші (на даний момент) і буде жахливо для рідких наборів. Зокрема, створення порожнього набору домінує над усіма іншими операціями (для розріджених наборів). Кнут, до речі, вирішує це питання в TAoCP.
Рафаель

12 МБ? так? Плакат сказав, що у нього є лише 2 набори. плакат не вказав рідкість / щільність його набору. на це вказується у моїй відповіді. Ви припускаєте, що у нього рідкісні набори? Немає одного правильного відповіді, підхід вказаний як альтернативний варіант, який може бути корисним залежно від обставин. це не рідко використовується в цьому контексті ...
vzn

10101061010b1.15GB
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.