Знаходження пари бітових векторів, що не перекриваються


17

Я даю вам список з n бітвекторів шириною k . Ваша мета - повернути двох бітвекторів зі списку, які не мають спільного 1, або ж повідомити про відсутність такої пари.

Наприклад, якщо я даю вам [00110,01100,11000] єдине рішення - {00110,11000} . Як варіант, вхід [111,011,110,101] не має рішення. І будь-який список, який містить повністю нульовий бітвектор 000...0 та інший елемент e має тривіальне рішення {e,000...0} .

Ось дещо складніший приклад, без рішення (кожен рядок є трохи векторним, чорні квадрати - 1s, а білі - 0s):

■ ■ ■ ■ □ □ □ □ □ □ □ □ □
■ □ □ □ ■ ■ ■ □ □ □ □ □ □ 
■ □ □ □ □ □ □ ■ ■ ■ □ □ □
■ □ □ □ □ □ □ □ □ □ ■ ■ ■
□ ■ □ □ □ ■ □ □ □ ■ ■ □ □
□ ■ □ □ ■ □ □ □ ■ □ □ □ ■
□ ■ □ □ □ □ ■ ■ □ □ □ ■ □ <-- All row pairs share a black square
□ □ ■ □ □ □ ■ □ ■ □ ■ □ □
□ □ ■ □ □ ■ □ ■ □ □ □ □ ■
□ □ ■ □ ■ □ □ □ □ ■ □ ■ □
□ □ □ ■ ■ □ □ ■ □ □ ■ □ □
□ □ □ ■ □ □ ■ □ □ ■ □ □ ■
□ □ □ ■ □ ■ □ □ ■ □ □ ■ □

Наскільки ефективно можна знайти два бітректори, що не перекриваються, або показати, що вони не існують?

Наївний алгоритм, де ви просто порівнюєте кожну можливу пару, є O(n2k) . Чи можна зробити краще?


Можливе зменшення: у вас є графік з однією вершиною для кожного вектора і ребром між двома вершинами, якщо два відповідних вектора мають спільне 1. Ви хочете знати, чи є діаметр графіка 2 . Але здається, що важко йти швидше, ніж O ( n 2 k ) . G2O(n2k)
Франсуа

@ FrançoisGodi Будь-який з'єднаний графічний компонент з трьома вузлами та відсутнім краєм має діаметр принаймні два. Для подання списку суміжності потрібен час, щоб перевірити це. O(V)
Крейг Гідні

@Strilanc Звичайно, якщо немає рішення, графік завершений (більш чіткий, ніж діаметр = 1, ви праві), але обчислення представлення списку суміжності може бути довгим.
Франсуа

На менша ширина слова вашої машини? k
Рафаель

1
@TomvanderZanden Це здається, що це порушить інваріанти, на які, ймовірно, покладається структура даних. Зокрема, ця рівність повинна бути перехідною. Я вже думав про використання трійки, і не бачу, як уникнути вибуху фактора-2 щоразу, коли маска запиту має 0.
Крейг Гідні

Відповіді:


10

Розминка: випадкові бітректори

Як розминка, ми можемо почати з того випадку, коли кожен бітвектор вибирається в однаковому порядку рівномірно. Тоді виявляється, що задачу можна вирішити за час (точніше, 1,6 можна замінити на lg 3 ).O(n1.6min(k,lgn))1.6lg3

Ми розглянемо наступний двоскладовий варіант проблеми:

З урахуванням безлічі з бітвекторов, визначити , де існує неперекривающійся пари сек S , T T .S,T{0,1}ksS,tT

Основний прийом для вирішення цього питання - розділити і перемогти. Ось алгоритм часу використанням ділення та перемоги:O(n1.6k)

  1. Розділити і T на основі першого положення біта. Іншими словами, форма S 0 = { s S : s 0 = 0 } , S 1 = { s S : s 0 = 1 } , T 0 = { t T : t 0 = 0 } , T 1 = { t T : tSTS0={sS:s0=0}S1={sS:s0=1}T0={tT:t0=0} .T1={tT:t0=1}

  2. Тепер рекурсивно шукайте пару, що не перекривається, від , від S 0 , T 1 і від T 1 , S 0 . Якщо будь-який рекурсивний виклик знайде пару, що не перекривається, виведіть його, інакше виведіть "Немає пари, що перекривається".S0,T0S0,T1T1,S0

Оскільки всі бітвектори вибираються випадковим чином, ми можемо очікувати та | Т б | | Т | / 2 . Таким чином, у нас є три рекурсивні дзвінки, і ми зменшили розмір проблеми в два рази (обидва набори зменшуються в розмірі в два рази). Після розбиття lg min ( | S | , | T | ) одна з двох множин зменшується до розміру 1, і проблему можна вирішити за лінійним часом. Ми отримуємо відношення рецидиву за лініями|Sb||S|/2|Tb||T|/2lgmin(|S|,|T|) , рішення якого T ( n ) = O ( n 1,6 k ) . Точніше, враховуючи час роботи у двоскладному випадку, ми бачимо, що час виконання - O ( хв ( | S | , | T | ) 0,6 макс. ( | S | , | T)T(n)=3T(n/2)+O(nk)T(n)=O(n1.6k) .O(min(|S|,|T|)0.6max(|S|,|T|)k)

Це можна вдосконалити, зазначивши, що якщо , то ймовірність існування пари, що не перекривається, експоненціально мала. Зокрема, якщо х , у двох випадкових векторів, ймовірність того, що вони не перекриваються є ( 3 / 4 ) K . Якщо | S | = | Т | = n , є n 2 таких пар, тому при зв'язанні об'єднанням ймовірність існування пари, що не перекривається, становить щонайбільше n 2 ( 3k2.5lgn+100x,y(3/4)k|S|=|T|=nn2 . Коли до 2,5 Л.Г. п + 100 , це1 / 2 100 . Отже, як крок перед обробкою, якщо k 2,5 lg n + 100 , ми можемо негайно повернути "Не існує пари, що не перекривається," (ймовірність, що це неправильно, незначно мала), інакше ми запустимо вищевказаний алгоритм.n2(3/4)kk2.5lgn+1001/2100k2.5lgn+100

Таким чином , ми досягаємо час роботи (або O ( хв ( | S | , | Т | ) 0,6 Макс ( | S | , | Т | ) хв ( K , Lg п ) ) для двох заданих варіантів, запропонованих вище), для особливого випадку, коли біттектори вибираються рівномірно випадково.O(n1.6min(k,lgn))O(min(|S|,|T|)0.6max(|S|,|T|)min(k,lgn))

Звичайно, це не найгірший аналіз. Випадкові bitvectors значно простіше, ніж найгірший випадок - але давайте розглянемо це як розминку, щоб отримати деякі ідеї, які, можливо, ми можемо застосувати до загального випадку.

Уроки розминки

Ми можемо засвоїти кілька уроків з розминки вище. По-перше, корисним є поділ і перемогу (розкол на бітову позицію). По-друге, ви хочете розділити на бітову позицію якомога більше -х у цій позиції; чим більше їх у 0 , тим менше зменшення розміру підпроблеми.10

По-третє, це говорить про те, що проблема стає все складніше, коли щільність s зменшується - якщо серед бітвекторів дуже мало 1 -х (вони в основному 0 '), проблема виглядає досить важко, оскільки кожен розкол зменшується розмір підпроблем небагато. Отже, визначте, що щільність Δ є часткою бітів, що дорівнює 1 (тобто, з усіх n k біт), а щільність бітового положення i - частка бітвекторів, що є 1 у положенні i .110Δ1nki1i

Поводження з дуже низькою щільністю

На наступному кроці ми можемо задатися питанням, що станеться, якщо щільність надзвичайно мала. Виявляється, якщо щільність у кожному бітовому положенні менша за , ми гарантуємо, що існує пара, що не перекривається: є аргумент існування (неконструктивний), який показує, що повинна існувати якась пара, що не перекривається. Це не допомагає нам його знайти, але, принаймні, ми знаємо, що воно існує.1/k

Чому це так? Скажімо , що пара бітвекторов буде покрита на бітової позиції я , якщо х я = у я = 1 . Зверніть увагу, що кожна пара бітректорів, що перекриваються, повинна бути охоплена деяким положенням бітів. Тепер, якщо ми зафіксуємо конкретне бітове положення i , кількість пар, які можуть бути охоплені цим бітовим положенням, становить щонайбільше ( n Δ ( i ) ) 2 < n 2 / k . Підведення підсумків по всіх kx,yixi=yi=1i(nΔ(i))2<n2/kkз бітових позицій ми знаходимо, що загальна кількість пар, охоплених деяким бітовим положенням, становить . Це означає, що повинна існувати пара, яка не охоплена жодною бітовою позицією, що означає, що ця пара не перекривається. Отже, якщо щільність є достатньо низькою у кожному бітовому положенні, то, безумовно, існує пара.<n2

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

До алгоритму загального випадку

У загальному випадку, схоже, природний евристичний виглядає так: виберіть бітове положення з найбільшою кількістю 1 's (тобто з найбільшою щільністю), і розділіть його. Іншими словами:i1

  1. Знайдіть бітове положення яке максимізує Δ ( i ) .iΔ(i)

  2. Розщеплення і T на основі бітового положення i . Іншими словами, форма S 0 = { s S : s i = 0 } , S 1 = { s S : s i = 1 } , T 0 = { t T : t i = 0 } , T 1 = { t T :STiS0={sS:si=0}S1={sS:si=1}T0={tT:ti=0} .T1={tT:ti=1}

  3. Тепер рекурсивно шукайте пару, що не перекривається, від , від S 0 , T 1 і від T 1 , S 0 . Якщо будь-який рекурсивний виклик знайде пару, що не перекривається, виведіть його, інакше виведіть "Немає пари, що перекривається".S0,T0S0,T1T1,S0

Завдання полягає в аналізі його ефективності в гіршому випадку.

Припустимо, що в якості етапу попередньої обробки спочатку обчислюємо щільність кожної бітової позиції. Також, якщо для кожногоi, припустимо, що етап попередньої обробки виводить "Пара, що перекривається, існує" (я розумію, що це не є прикладом пари, що перекривається, але давайте відкладемо це як окремий виклик). Все це можна зробити заO(nk)час. Інформацію про щільність можна ефективно підтримувати, коли ми робимо рекурсивні дзвінки; це не буде домінуючим фактором часу роботи.Δ(i)<1/kiO(nk)

Яким буде час роботи цієї процедури? Я не впевнений, але ось кілька спостережень, які можуть допомогти. Кожен рівень рекурсії зменшує розмір проблеми приблизно на bitvectors (наприклад, відnbitvectors доn-n/n/kn bitvectors). Тому рекурсія може тривати лишеnn/k рівні глибокі. Однак я не одразу впевнений, як підрахувати кількість листя в дереві рекурсії (їх набагато менше3k відходить), тому я не впевнений, до якого часу роботи це повинно призвести.3k


реклама низької щільності: це, здається, якийсь аргумент з голубиною норою. Може, якщо ми використаємо вашу загальну ідею (розділити wrt стовпець на більшість), ми отримаємо кращі межі, оскільки -case (ми не повторюємось) вже позбавляється від "більшості"? (S1,T1)
Рафаель

Загальна кількість одиниць може бути корисним параметром. Ви вже показали нижню межу, яку ми можемо використовувати для вирубки дерева; чи можемо ми також показати верхні межі? Наприклад, якщо їх більше, ніж , ми маємо принаймні c перекриття. ckc
Рафаель

До речі, як ви пропонуєте зробити перший розкол; довільно? Чому б просто не розділити весь вхідний набір wrt деякого стовпця ? Нам потрібно лише повторити в 0 -казі (серед тих, хто поділяє один на i ), немає рішення . В очікуванні це дає через T ( n ) = T ( n / 2 ) + O ( n k ) межу O ( n k ) (якщо k фіксовано). Для загальної межі ви показали, що ми можемо (припускаючи нижню межу відрізання, яку ви пропонуєте), щоб ми позбулися хоча бi0iT(n)=T(n/2)+O(nk)O(nk)k елементів з кожним розщепленням, що, мабуть, означає, щоO(nk) пов'язаний вгіршому випадку. Або я щось пропускаю? n/kO(nk)
Рафаель

Ах, це неправильно, звичайно, оскільки він не враховує 0-1-невідповідності. Це я отримую, намагаючись думати перед сніданком.
Рафаель

@Raphael, є два питання: (а) вектори можуть бути в основному нулями, тому ви не можете розраховувати на отримання розколу 50-50; повтор буде чимось більше схожим на , (b) важливіше, недостатньо просто повторювати 0-підмножину; Вам також потрібно вивчити пари між вектором з 0-підмножини та вектором з 1-підмножини, щоб зробити додаткову рекурсію або два. (Я думаю? Я сподіваюся, що я зрозумів це правильно.)T(n)=T((nn/k)k)+O(nk)
DW

8

Швидше рішення, коли , використовуючи матричне множенняnk

Припустимо, що . Наша мета - зробити краще, ніж час роботи O ( n 2 k ) = O ( n 3 ) .n=kO(n2k)=O(n3)

Ми можемо вважати біттектори та бітові позиції як вузли на графіку. Між вузлом біттерактора і вузлом бітового положення є край, коли бітвектор має в цьому положенні 1. Отриманий графік є двостороннім (з вузлами, що представляють бітвектор, з одного боку та вузлами, що представляють бітопозицію, з іншого), і має вузлів.n+k=2n

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

Квадратування матриці nxn може бути виконано за час , де ω, як відомо, знаходиться під 2.373, а передбачається, що це 2 .O(nω)ω2.3732

Отже алгоритм:

  • Перетворіть бітвектори та бітові позиції у двосторонній графік з вузлами та не більше n k ребрами. Це займає час O ( n k ) .n+knkO(nk)
  • Обчисліть матрицю суміжності графіка. Це займає час і простір.O((n+k)2)
  • Квадрат матриці суміжності. Це займає час .O((n+k)ω)
  • Шукайте в розділі бітвектора матриці суміжності нульові записи. Це займає час .O(n2)

Найдорожчим етапом є зіставлення матриці суміжності. Якщо то загальний алгоритм займає час O ( ( n + k ) ω ) = O ( n ω ) , що краще, ніж наївний O ( n 3 ) час.n=kO((n+k)ω)=O(nω)O(n3)

Це рішення також швидше, коли зростає не надто-значно-повільніше і не-надто-швидше, ніж n . Доки k Ω ( n ω - 2 ) і k O ( n 2)knkΩ(nω2), тоді(n+k)ωкращеn2k. Дляw2,337,що означаєn0,731kn1,337(асимптотично). Якщоwобмежується до 2, то межі розширюються доnϵkn2-ϵ.kO(n2ω1)(n+k)ωn2kw2.373n0.731kn1.373wnϵkn2ϵ


1. Це також краще, ніж наївне рішення, якщо але k = o ( n 1.457 ) . 2. Якщо k n , евристичним може бути: вибрати випадкову підмножину з n бітових позицій, обмежитись цими бітовими позиціями та використовувати матричне множення, щоб перерахувати всі пари, які не перетинаються в цих n бітових позиціях; для кожної такої пари перевірте, чи вирішує вона вихідну проблему. Якщо не так багато пар, які не перетинаються в цих nk=Ω(n)k=o(n1.457)knnnn bit positions, this provides a speedup over the naive algorithm. However I don't know a good upper bound on the number of such pairs.
D.W.

4

This is equivalent to finding a bit vector which is a subset of the complement of another vector; ie its 1's occur only where 0's occur in the other.

If k (or the number of 1's) is small, you can get O(n2k) time by simply generating all the subsets of the complement of each bitvector and putting them in a trie (using backtracking). If a bitvector is found in the trie (we can check each before complement-subset insertion) then we have a non-overlapping pair.

If the number of 1's or 0's is bounded to an even lower number than k, then the exponent can be replaced by that. The subset-indexing can be on either each vector or its complement, so long as probing uses the opposite.

o(k)o(2k) searches.


n2(1p)k, where p is the probability of 1's in the bitvector. A couple of implementation details: though this is a slight improvement, there's no need to compute and store the complements in the trie. Just following the complementary branches when checking for a non-overlapping match is enough. And, taking the 0's directly as wildcards, no special wildcard is needed, either.
Mauro Lacy

2

Represent the bit vectors as an n×k matrix M. Take i and j between 1 and n.

(MMT)ij=lMilMjl.

(MMT)ij, the dot product of the ith and jth vector, is non-zero if, and only if, vectors i and j share a common 1. So, to find a solution, compute MMT and return the position of a zero entry, if such an entry exists.

Complexity

Using naive multiplication, this requires O(n2k) arithmetic operations. If n=k, it takes O(n2.37) operations using the utterly impractical Coppersmith-Winograd algorithm, or O(n2.8) using the Strassen algorithm. If k=O(n0.302), then the problem may be solved using n2+o(1) operations.


How is this different from Strilanc's answer?
D.W.

1
@D.W. Using an n-by-k matrix instead of an (n+k)-by-(n+k) matrix is an improvement. Also it mentions a way to cut off the factor of k when k << n, so that might be useful.
Craig Gidney
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.