Trovare una coppia di vettori di bit non sovrapposti


17

Ti do un elenco di bitvector di larghezza . Il tuo obiettivo è quello di restituire due bitvector dall'elenco che non hanno 1 in comune, oppure di segnalare che non esiste tale coppia.knk

Ad esempio, se ti do l'unica soluzione è . In alternativa, l'ingresso non ha soluzione. E qualsiasi elenco che contiene il bitvector tutto-zero e un altro elemento ha una soluzione banale .{ 00110 , 11000 } [ 111 , 011 , 110 , 101 ] 000 ... 0 e { e , 000 ... 0 }[00110,01100,11000]{00110,11000}[111,011,110,101]000...0e{e,000...0}

Ecco un esempio leggermente più difficile, senza soluzione (ogni riga è un po 'vettoriale, i quadrati neri sono 1 e i quadrati bianchi sono 0):

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

Con quale efficienza è possibile trovare o visualizzare due non bitvector non sovrapposti?

L'algoritmo ingenuo, in cui si confronta semplicemente ogni coppia possibile, è . È possibile fare di meglio?O(n2k)


Una possibile riduzione: hai un grafico con un vertice per ogni vettore e un bordo tra due vertici se i due vettori corrispondenti hanno un 1 in comune. Vuoi sapere se il diametro del grafico è . Ma sembra difficile andare più veloce di . 2 O ( n 2 k )G2O(n2k)
François,

@ FrançoisGodi Qualsiasi componente grafico collegato con tre nodi e un bordo mancante ha un diametro di almeno due. Con una rappresentazione dell'elenco di adiacenza, ci vuole tempo per verificarlo. O(V)
Craig Gidney,

@Strilanc Certo, se non c'è soluzione il grafico è completo (più chiaro di diametro = 1, hai ragione), ma il calcolo della rappresentazione dell'elenco di adiacenza potrebbe essere lungo.
François,

è più piccolo della larghezza della parola della tua macchina? k
Raffaello

1
@TomvanderZanden Sembra che ciò violerebbe gli invarianti su cui probabilmente si basa la struttura dei dati. In particolare, tale uguaglianza dovrebbe essere transitiva. Ho già pensato di usare un trie e non vedo come evitare un ingrandimento del fattore 2 ogni volta che la maschera di bit della query ha uno 0.
Craig Gidney,

Risposte:


10

Warmup: bitvector casuali

Come warm-up, possiamo iniziare dal caso in cui ogni bitvector viene scelto in modo uniforme a caso. Quindi si scopre che il problema può essere risolto in tempo (più precisamente, può essere sostituito con ).1,6 lg 3O(n1.6min(k,lgn))1.6lg3

Prenderemo in considerazione la seguente variante in due serie del problema:

Insiemi trovati di bitvectors, determinare dove esiste senza sovrapposizione pair . s S , t TS,T{0,1}ksS,tT

La tecnica di base per risolvere questo problema è dividere e conquistare. Ecco un algoritmo che usa divide-and-conquer:O(n1.6k)

  1. Dividi e base alla posizione del primo bit. In altre parole, forma , , , .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 : t 0 = 1 }STS0={sS:s0=0}S1={sS:s0=1}T0={tT:t0=0}T1={tT:t0=1}

  2. Ora ricorsivamente cercare una coppia non sovrapposta da , da e da . Se una chiamata ricorsiva trova una coppia non sovrapposta, emettila in uscita, altrimenti produce "Non esiste una coppia sovrapposta".S 0 , T 1 T 1 , S 0S0,T0S0,T1T1,S0

Poiché tutti i bitvector sono scelti a caso, possiamo aspettarci e . Pertanto, abbiamo tre chiamate ricorsive e abbiamo ridotto la dimensione del problema di un fattore due (entrambe le serie sono ridotte di un fattore due). Dopo la divisione di , uno dei due set scende alla dimensione 1 e il problema può essere risolto in tempo lineare. Otteniamo una relazione di ricorrenza lungo le linee di , la cui soluzione è . Considerando il tempo di esecuzione in modo più preciso nel caso di due set, vediamo che il tempo di esecuzione è .| T b | | T | / 2 lg min ( | S | , | T | ) T ( n ) = 3 T ( n / 2 ) + O ( n k ) T ( n ) = O ( n 1.6 k ) O|Sb||S|/2|Tb||T|/2lgmin(|S|,|T|)T(n)=3T(n/2)+O(nk)T(n)=O(n1.6k)O(min(|S|,|T|)0.6max(|S|,|T|)k)

Questo può essere ulteriormente migliorato, osservando che se , la probabilità che esista una coppia non sovrapposta è esponenzialmente piccola. In particolare, se sono due vettori casuali, la probabilità che non si sovrappongano è . Se , ci sono tali coppie, quindi per un limite di unione, la probabilità che esista una coppia non sovrapposta è al massimo . Quando , questo è . Quindi, come fase di pre-elaborazione, sex , y ( 3 / 4 ) k | S | = | T | = N n 2 n 2 ( 3 / 4 ) k k 2.5 lg n + 100 1 / 2 100 k 2.5 lg n + 100k2.5lgn+100x,y(3/4)k|S|=|T|=nn2n2(3/4)kk2.5lgn+1001/2100k2.5lgn+100, quindi possiamo immediatamente restituire "Non esiste alcuna coppia non sovrapposta" (la probabilità che ciò non sia corretto è trascurabilmente piccola), altrimenti eseguiamo l'algoritmo sopra.

In questo modo otteniamo un tempo di esecuzione di (o per la variante a due set proposta sopra), per il caso speciale in cui i bitvector vengono scelti in modo uniforme a caso.O ( min ( | S | , | T | ) 0,6 max ( | S | , | T | ) min ( k , lg n ) )O(n1.6min(k,lgn))O(min(|S|,|T|)0.6max(|S|,|T|)min(k,lgn))

Naturalmente, questa non è un'analisi del caso peggiore. I bitvector casuali sono notevolmente più facili del caso peggiore, ma trattiamolo come un riscaldamento, per avere alcune idee che forse possiamo applicare al caso generale.

Lezioni dal riscaldamento

Possiamo imparare alcune lezioni dal riscaldamento sopra. In primo luogo, dividere e conquistare (dividere in una piccola posizione) sembra utile. In secondo luogo, si desidera dividere in una posizione bit con il maggior numero di in quella posizione possibile; più ci sono, minore è la riduzione della dimensione del sottoproblema.010

In terzo luogo, ciò suggerisce che il problema si aggrava man mano che la densità di diminuisce - se ci sono pochissimi tra i bitvector (sono principalmente ), il problema sembra piuttosto difficile, poiché ogni divisione si riduce la dimensione dei sottoproblemi un po '. Quindi, definire la densità come la frazione di bit che sono (cioè, tra tutti i bit ) e la densità della posizione dei bit per essere la frazione di bitvector che sono nella posizione .1 0 Δ 1 n k i 1 i110Δ1nki1i

Gestione di una densità molto bassa

Come passo successivo, potremmo chiederci cosa succede se la densità è estremamente piccola. Si scopre che se la densità in ogni posizione di bit è inferiore a , ci viene garantito che esiste una coppia non sovrapposta: esiste un argomento di esistenza (non costruttiva) che mostra che alcuni non sovrapposti la coppia deve esistere. Questo non ci aiuta a trovarlo, ma almeno sappiamo che esiste.1/k

Perché è così? Diciamo che una coppia di bitvector è coperta dalla posizione bit se . Si noti che ogni coppia di bitvector sovrapposti deve essere coperta da una posizione di bit. Ora, se fissiamo una particolare posizione di bit , il numero di coppie che possono essere coperte da quella posizione di bit è al massimo . Sommando tutte le delle posizioni dei bit, troviamo che il numero totale di coppie coperte da una certa posizione dei bit èi x i = y i = 1 i ( n Δ ( i ) ) 2 < n 2 / k k < n 2x,yixi=yi=1i(nΔ(i))2<n2/kk<n2. Ciò significa che deve esistere una coppia che non è coperta da alcuna posizione di bit, il che implica che questa coppia non si sovrappone. Quindi se la densità è sufficientemente bassa in ogni posizione di bit, allora sicuramente esiste una coppia non sovrapposta.

Tuttavia, non riesco a identificare un algoritmo veloce per trovare una coppia così non sovrapposta, in questi regimi, anche se ne è garantito uno. Non vedo immediatamente alcuna tecnica che produrrebbe un tempo di esecuzione che ha una dipendenza sub-quadratica da . Quindi, questo è un bel caso speciale su cui concentrarsi, se vuoi passare un po 'di tempo a pensare a questo problema.n

Verso un algoritmo del caso generale

Nel caso generale, un euristico naturale sembra essere: scegliere la posizione del bit con il maggior numero di (ovvero, con la più alta densità) e dividerlo. In altre parole:1i1

  1. Trova un po 'la posizione che massimizza .Δ ( i )iΔ(i)

  2. Dividi e base alla posizione dei bit . In altre parole, forma , , , .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 : t i = 1 }STiS0={sS:si=0}S1={sS:si=1}T0={tT:ti=0}T1={tT:ti=1}

  3. Ora ricorsivamente cercare una coppia non sovrapposta da , da e da . Se una chiamata ricorsiva trova una coppia non sovrapposta, emettila in uscita, altrimenti produce "Non esiste una coppia sovrapposta".S 0 , T 1 T 1 , S 0S0,T0S0,T1T1,S0

La sfida è analizzare le sue prestazioni nel peggiore dei casi.

Supponiamo che come fase di pre-elaborazione calcoliamo prima la densità di ogni posizione di bit. Inoltre, se per ogni , supponiamo che la fase di pre-elaborazione produca "Esiste una coppia sovrapposta" (mi rendo conto che questo non mostra un esempio di coppia sovrapposta, ma mettiamolo da parte come una sfida separata). Tutto ciò può essere fatto in tempo. Le informazioni sulla densità possono essere gestite in modo efficiente mentre eseguiamo chiamate ricorsive; non sarà il contributo dominante al tempo di esecuzione. iO(nk)Δ(i)<1/kiO(nk)

Quale sarà il tempo di esecuzione di questa procedura? Non ne sono sicuro, ma qui ci sono alcune osservazioni che potrebbero aiutare. Ogni livello di ricorsione riduce la dimensione del problema di circa bitvector (ad esempio, da bitvector a bitvector). Pertanto, la ricorsione può raggiungere solo livelli di . Tuttavia, non sono immediatamente sicuro di come contare il numero di foglie nell'albero di ricorsione (ci sono molte meno di foglie), quindi non sono sicuro del tempo di esecuzione che dovrebbe portare per. nn-n/n/knnn/k 3k3k


annuncio a bassa densità: questo sembra essere una sorta di argomento sul buco del piccione. Forse se usiamo la tua idea generale (dividere la colonna con la maggior parte), otteniamo limiti migliori perché la -case (a cui non facciamo riferimento) si sbarazza già di "la maggior parte"? (S1,T1)
Raffaello

Il numero totale di quelli può essere un parametro utile. Hai già mostrato un limite inferiore che possiamo usare per tagliare l'albero; possiamo mostrare anche i limiti superiori? Ad esempio, se ce ne sono più di , abbiamo almeno c sovrapposizioni. ckc
Raffaello

A proposito, come proponi di fare la prima divisione; arbitrariamente? Perché non dividere l'intero set di input con una colonna ? Dobbiamo solo ricorrere alla 0- case (non esiste una soluzione tra quelle che condividono una a i ). Nell'aspettativa, ciò dà a T ( n ) = T ( n / 2 ) + O ( n k ) un limite di O ( n k ) (se k fisso). Per un limite generale, hai dimostrato che possiamo (supponendo il limite inferiore che proponi) di cui ci liberiamo almenoi0iT(n)=T(n/2)+O(nk)O(nk)k elementi con ogni divisione, il che sembra implicare unO(nk) nelcaso peggiore. Oppure mi sfugge qualcosa? n/kO(nk)
Raffaello

Ah, è sbagliato, ovviamente, dal momento che non considera i disallineamenti 0-1. Questo è quello che ottengo provando a pensare prima di colazione, immagino.
Raffaello

@Raphael, ci sono due problemi: (a) i vettori potrebbero essere prevalentemente zeri, quindi non puoi contare su una divisione 50-50; la ricorrenza sarebbe qualcosa di più simile a , (b) ancora più importante, non è sufficiente ricorrere sul sottoinsieme 0; devi anche esaminare gli accoppiamenti tra un vettore del sottoinsieme 0 e un vettore del sottoinsieme 1, quindi c'è una ricorsione in più o due da fare. (Penso? Spero di aver capito bene.)T(n)=T((nn/k)k)+O(nk)
DW

8

Soluzione più veloce quando , utilizzando la moltiplicazione della matricenk

Supponiamo che . Il nostro obiettivo è fare meglio di un tempo di esecuzione O ( n 2 k ) = O ( n 3 ) .n=kO(n2k)=O(n3)

Possiamo pensare ai bitvector e alle posizioni dei bit come nodi in un grafico. C'è un limite tra un nodo bitvector e un nodo posizione bit quando il bitvector ha un 1 in quella posizione. Il grafico risultante è bipartito (con i nodi che rappresentano i bitvector su un lato e i nodi che rappresentano i bitpositivi sull'altro) e ha nodi.n+k=2n

Data la matrice di adiacenza di un grafico, possiamo dire se esiste un percorso a due hop tra due vertici quadrando M e controllando se la matrice risultante ha un "bordo" tra quei due vertici (cioè l'ingresso del bordo nella matrice quadrata è diverso da zero). Per i nostri scopi, una voce zero nella matrice di adiacenza quadrata corrisponde a una coppia di bitvector non sovrapposti (cioè una soluzione). La mancanza di zero significa che non esiste soluzione.M M

La quadratura di una matrice nxn può essere eseguita in tempo , dove ω è noto per essere inferiore a 2.373 e si ipotizza che sia 2 .O(nω)ω2.3732

Quindi l'algoritmo è:

  • Convertire i bitvectors e le posizioni di bit in un grafo bipartito con nodi e al massimo n k bordi. Questo richiede tempo O ( n k ) .n+knkO(nk)
  • Calcola la matrice di adiacenza del grafico. Questo richiede tempo e spazio.O((n+k)2)
  • Square la matrice di adiacenza. Questo richiede tempo .O((n+k)ω)
  • Cerca nella sezione bitvector della matrice di adiacenza per zero voci. Questo richiede tempo .O(n2)

Il passo più costoso è la quadratura della matrice di adiacenza. Se l'algoritmo complessivo richiede O ( ( n + k ) ω ) = O ( n ω ) tempo, che è migliore del tempo ingenuo O ( n 3 ) .n=kO((n+k)ω)=O(nω)O(n3)

Questa soluzione è anche più veloce quando diventa non troppo lento e non troppo veloce di n . Finché k Ω ( n ω - 2 ) e k O ( n 2knkΩ(nω2), quindi(n+k)ωè migliore din2k. Perw2.373che si traduce inn0.731kn1.373(asintoticamente). Sewlimita a 2, i limiti si allargano versonϵkn2-ϵ.kO(n2ω1)(n+k)ωn2kw2.373n0.731kn1.373wnϵkn2ϵ


1. Questo è anche meglio della soluzione ingenua se ma k = o ( n 1.457 ) . 2. Se k n , un euristico potrebbe essere: scegliere un sottoinsieme casuale di n posizioni di bit, limitarsi a quelle posizioni di bit e utilizzare la moltiplicazione della matrice per enumerare tutte le coppie che non si sovrappongono in quelle posizioni di n bit; per ciascuna di queste coppie, controlla se risolve il problema originale. Se non ci sono molte coppie che non si sovrappongono in quelle nk=Ω(n)k=o(n1.457)knnnnposizioni di bit, questo fornisce una velocità sull'algoritmo ingenuo. Tuttavia non conosco un buon limite superiore per il numero di tali coppie.
DW

4

Ciò equivale a trovare un vettore di bit che è un sottoinsieme del complemento di un altro vettore; cioè i suoi 1 si verificano solo dove si verificano 0 nell'altro.

Se k (o il numero di 1) è piccolo, puoi ottenere il tempo semplicemente generando tutti i sottoinsiemi del complemento di ciascun bitvector e inserendoli in un trie (usando il backtracking). Se nel trie viene trovato un bitvector (possiamo verificarne ciascuno prima dell'inserimento del complemento-sottoinsieme), allora abbiamo una coppia non sovrapposta.O(n2k)

Se il numero di 1 o 0 è limitato a un numero anche inferiore a k, l'esponente può essere sostituito da quello. L'indicizzazione dei sottoinsiemi può essere su ciascun vettore o sul suo complemento, purché il sondaggio usi il contrario.

C'è anche uno schema per la ricerca di superset in un trie che memorizza ogni vettore solo una volta, ma fa saltare i bit durante le sonde per quella che credo sia una complessità aggregata simile; cioè ha inserimento ma o ( 2 k ) ricerche.o(k)o(2k)


Grazie. La complessità della tua soluzione è , dove p è la probabilità di 1 nel bitvector. Un paio di dettagli di implementazione: sebbene si tratti di un leggero miglioramento, non è necessario calcolare e archiviare i complementi nel trie. È sufficiente seguire le diramazioni complementari quando si verifica una corrispondenza non sovrapposta. E, prendendo gli 0 direttamente come caratteri jolly, non è nemmeno necessario alcun carattere jolly speciale. n2(1p)kp
Mauro Lacy,

2

Rappresentano i vettori di bit come una matrice M . Prendi i e j tra 1 e n .n×kMijn

(MMT)ij=lMilMjl.

, il prodotto scalare del I ° e j esimo vettore, è diverso da zero se, e solo se, i vettori i e j condividono un comune 1. Quindi, per trovare una soluzione, calcolare M M T e restituisce la posizione di una voce zero, se tale voce esiste.(MMT)ijijijMMT

Complessità

Usando la moltiplicazione ingenua, ciò richiede operazioni aritmetiche . Se n = k , prende le operazioni O ( n 2.37 ) usando l'algoritmo Coppersmith-Winograd assolutamente impraticabile, o O ( n 2.8 ) usando l'algoritmo Strassen. Se k = O ( n 0.302 ) , il problema può essere risolto utilizzando le operazioni n 2 + o ( 1 ) .O(n2k)n=kO(n2.37)O(n2.8)k=O(n0.302)n2+o(1)


In che cosa differisce dalla risposta di Strilanc ?
DW

1
@DW L'uso di una matrice -by- k anziché una matrice ( n + k ) -by- ( n + k ) è un miglioramento. Inoltre menziona un modo per tagliare il fattore k quando k << n, quindi potrebbe essere utile. nk(n+k)(n+k)
Craig Gidney,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.