Я даю вам список из битвекторов шириной . Ваша цель - вернуть два битовых вектора из списка, у которых нет общих единиц, или сообщить, что такой пары не существует.
Например, если я дам вам то единственным решением будет . В качестве альтернативы, вход не имеет решения. И любой список, который содержит нулевой битовый вектор и другой элемент имеет тривиальное решение .
Вот немного более сложный пример без решения (каждая строка является битовым вектором, черные квадраты равны 1 с, а белые квадраты равны 0):
■ ■ ■ ■ □ □ □ □ □ □ □ □ □
■ □ □ □ ■ ■ ■ □ □ □ □ □ □
■ □ □ □ □ □ □ ■ ■ ■ □ □ □
■ □ □ □ □ □ □ □ □ □ ■ ■ ■
□ ■ □ □ □ ■ □ □ □ ■ ■ □ □
□ ■ □ □ ■ □ □ □ ■ □ □ □ ■
□ ■ □ □ □ □ ■ ■ □ □ □ ■ □ <-- All row pairs share a black square
□ □ ■ □ □ □ ■ □ ■ □ ■ □ □
□ □ ■ □ □ ■ □ ■ □ □ □ □ ■
□ □ ■ □ ■ □ □ □ □ ■ □ ■ □
□ □ □ ■ ■ □ □ ■ □ □ ■ □ □
□ □ □ ■ □ □ ■ □ □ ■ □ □ ■
□ □ □ ■ □ ■ □ □ ■ □ □ ■ □
Насколько эффективно можно найти два непересекающихся битовых вектора или показать, что они не существуют?
Наивным алгоритмом, где вы просто сравниваете каждую возможную пару, является . Можно ли сделать лучше?
algorithms
search-algorithms
Крейг Гидни
источник
источник
Ответы:
Разминка: случайные битовые векторы
В качестве разминки мы можем начать со случая, когда каждый битовый вектор выбирается равномерно случайным образом. Тогда оказывается, что проблема может быть решена заO(n1.6min(k,lgn)) времени (точнее, 1.6 можно заменить на lg3 ).
Мы рассмотрим следующий двунаправленный вариант задачи:
С учетом множества из битвекторов, определить , где существует неперекрывающийся пары сек ∈ S , T ∈ T .S,T⊆{0,1}k s∈S,t∈T
Основной метод решения этой проблемы - разделяй и властвуй. Вот алгоритм времени , использующий принцип «разделяй и властвуй»:O(n1.6k)
Разделите и 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 : tS T S0={s∈S:s0=0} S1={s∈S:s0=1} T0={t∈T:t0=0} .T1={t∈T:t0=1}
Теперь рекурсивно ищем неперекрывающуюся пару из , из S 0 , T 1 и из T 1 , S 0 . Если какой-либо рекурсивный вызов найдет неперекрывающуюся пару, выведите его, в противном случае выведите «Перекрывающаяся пара не существует».S0,T0 S0,T1 T1,S0
Поскольку все битовые векторы выбираются случайным образом, мы можем ожидать и | Т б | ≈ | T | / 2 . Таким образом, у нас есть три рекурсивных вызова, и мы уменьшили размер задачи в два раза (оба набора уменьшились в размере в два раза). После разбиения lg min ( | S | , | T | ) один из двух наборов уменьшается до размера 1, и проблема может быть решена за линейное время. Мы получаем рекуррентное соотношение в соответствии с|Sb|≈|S|/2 |Tb|≈|T|/2 lgmin(|S|,|T|) , чье решение T ( n ) = O ( n 1.6 k ) . Учет времени работы более точно в случае с двумя наборами, мы видим, что время работы составляет O ( мин ( | S | , | T | ) 0,6 макс ( | S | , | TT(n)=3T(n/2)+O(nk) T(n)=O(n1.6k) .O(min(|S|,|T|)0.6max(|S|,|T|)k)
Это можно еще улучшить, отметив, что если , то вероятность того, что непересекающаяся пара существует, экспоненциально мала. В частности, если х , у двух случайных векторов, вероятность того, что они не перекрывающиеся является ( 3 / 4 ) K . Если | S | = | T | = n , существует n 2 таких пар, так что по границе объединения вероятность того, что неперекрывающаяся пара существует, не превышает n 2 ( 3k≥2.5lgn+100 x,y (3/4)k |S|=|T|=n n2 . Когда к ≥ 2,5 Л.Г. п + 100 , это ≤ 1 / 2 100 . Таким образом, в качестве шага предварительной обработки, если k ≥ 2,5 lg n + 100 , мы можем сразу же вернуть «Не существует непересекающейся пары» (вероятность того, что это неверно, пренебрежимо мала), в противном случае мы запускаем описанный выше алгоритм.n2(3/4)k k≥2.5lgn+100 ≤1/2100 k≥2.5lgn+100
Таким образом, мы достигаем время работы (или O ( мин ( | S | , | T | ) 0,6 макс ( | S | , | T | ) min ( k , lg n) ) ) для предложенного выше варианта с двумя наборами), для частного случая, когда битовые векторы выбираются случайным образом равномерно.O(n1.6min(k,lgn)) O(min(|S|,|T|)0.6max(|S|,|T|)min(k,lgn))
Конечно, это не худший анализ. Случайные битовые векторы значительно проще, чем в худшем случае, но давайте рассмотрим это как разминку, чтобы получить некоторые идеи, которые, возможно, мы сможем применить к общему случаю.
Уроки разминки
Мы можем выучить несколько уроков из разминки выше. Во-первых, «разделяй и властвуй» (разделяя на битовую позицию) кажется полезным. Во- вторых, вы хотите разделить на позицию бита с таким количеством «S в таком положении , как это возможно; чем больше 0 , тем меньше уменьшение размера подзадачи.1 0
В- третьих, это говорит о том, что эта проблема становится все труднее , как плотность «S становится все меньше - если есть очень мало 1 » S среди битвекторов (они в основном 0 «s), проблема выглядит довольно сложно, так как каждый раскол уменьшает размер подзадач немного. Итак, определите плотность Δ, чтобы быть долей битов, которые равны 1 (то есть из всех n k битов), и плотность позиции бита i, чтобы быть долей векторов битов, которые равны 1 в позиции i .1 1 0 Δ 1 nk i 1 i
Обработка очень низкой плотности
В качестве следующего шага мы могли бы задаться вопросом, что произойдет, если плотность чрезвычайно мала. Оказывается, что если плотность в каждой позиции бита меньше , мы гарантируем, что существует неперекрывающаяся пара: существует (неконструктивный) аргумент существования, показывающий, что некоторая непересекающаяся пара должна существовать. Это не помогает нам найти его, но, по крайней мере, мы знаем, что оно существует.1/k−−√
Почему это так? Скажем , что пара битвекторов будет покрыта на битовой позиции я , если х я = у я = 1 . Обратите внимание, что каждая пара перекрывающихся битовых векторов должна быть покрыта некоторой битовой позицией. Теперь, если мы фиксируем конкретную битовую позицию i , количество пар, которые могут быть покрыты этой битовой позицией, составляет самое большее ( n Δ ( i ) ) 2 < n 2 / k . Суммирование по всем kx,y i xi=yi=1 i (nΔ(i))2<n2/k k из битовых позиций мы находим, что общее количество пар, которые покрыты некоторой битовой позицией, составляет . Это означает, что должна существовать некоторая пара, которая не покрыта какой-либо битовой позицией, что означает, что эта пара не перекрывается. Таким образом, если плотность достаточно низкая в каждой позиции бита, то непересекающаяся пара наверняка существует.<n2
Однако я затрудняюсь определить быстрый алгоритм для нахождения такой неперекрывающейся пары в этих режимах, хотя гарантированно существует такая. Я не сразу вижу какие-либо методы, которые дали бы время выполнения, которое имеет субквадратичную зависимость от . Итак, это хороший особый случай, на котором стоит сосредоточиться, если вы хотите потратить некоторое время на обдумывание этой проблемы.n
На пути к общему алгоритму
В общем случае естественная эвристика выглядит так: выберите битовую позицию с наибольшим числом 1 (т. Е. С наибольшей плотностью) и разделите ее. Другими словами:i 1
Найдите положение бита которое максимизирует Δ ( i ) .i Δ(i)
Разделить и 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 :S T i S0={s∈S:si=0} S1={s∈S:si=1} T0={t∈T:ti=0} .T1={t∈T:ti=1}
Теперь рекурсивно ищем неперекрывающуюся пару из , из S 0 , T 1 и из T 1 , S 0 . Если какой-либо рекурсивный вызов найдет неперекрывающуюся пару, выведите его, в противном случае выведите «Перекрывающаяся пара не существует».S0,T0 S0,T1 T1,S0
Задача состоит в том, чтобы проанализировать его производительность в худшем случае.
Давайте предположим, что в качестве шага предварительной обработки мы сначала вычислим плотность каждой позиции бита. Также, если для каждогоi, предположим, что на этапе предварительной обработки выводится «перекрывающаяся пара» (я понимаю, что это не демонстрирует пример перекрывающейся пары, но давайте отложим это как отдельную задачу). Все это можно сделать заO(nk)раз. Информация о плотности может поддерживаться эффективно, поскольку мы делаем рекурсивные вызовы; это не будет доминирующим фактором времени выполнения.Δ(i)<1/k−−√ i O(nk)
Какой будет продолжительность этой процедуры? Я не уверен, но вот несколько замечаний, которые могут помочь. Каждый уровень рекурсии уменьшает размер проблемы примерно на битовых векторов (например, отnбитовых векторов доn-n/ √n/k−−√ n битвекторов). Поэтому рекурсия может идти только о √n−n/k−−√ уровней глубоко. Тем не менее, я не сразу уверен, как посчитать количество листьев в дереве рекурсии (их намного меньше, чем3 √k−−√ уходит), поэтому я не уверен, к какому времени это должно привести.3k√
источник
Faster solution whenn≈k , using matrix multiplication
Suppose thatn=k . Our goal is to do better than an O(n2k)=O(n3) running time.
We can think of the bitvectors and bit positions as nodes in a graph. There is an edge between a bitvector node and a bit position node when the bitvector has a 1 in that position. The resulting graph is bipartite (with the bitvector-representing nodes on one side and the bitposition-representing nodes on the other), and hasn+k=2n nodes.
Given the adjacency matrixM of a graph, we can tell if there is a two-hop path between two vertices by squaring M and checking if the resulting matrix has an "edge" between those two vertices (i.e. the edge's entry in the squared matrix is non-zero). For our purposes, a zero entry in the squared adjacency matrix corresponds to a non-overlapping pair of bitvectors (i.e. a solution). A lack of any zeroes means there's no solution.
Squaring an n x n matrix can be done inO(nω) time, where ω is known to be under 2.373 and conjectured to be 2 .
So the algorithm is:
The most expensive step is squaring the adjacency matrix. Ifn=k then the overall algorithm takes O((n+k)ω)=O(nω) time, which is better than the naive O(n3) time.
This solution is also faster whenk grows not-too-much-slower and not-too-much-faster than n . As long as k∈Ω(nω−2) and k∈O(n2ω−1) , then (n+k)ω is better than n2k . For w≈2.373 that translates to n0.731≤k≤n1.373 (asymptotically). If w limits to 2, then the bounds widen towards nϵ≤k≤n2−ϵ .
источник
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 getO(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.
There's also a scheme for superset-finding in a trie that only stores each vector only once, but does bit-skipping during probes for what I believe is similar aggregate complexity; ie it haso(k) insertion but o(2k) searches.
источник
Represent the bit vectors as ann×k matrix M . Take i and j between 1 and n .
Complexity
Using naive multiplication, this requiresO(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.
источник