Struttura dei dati per l'intersezione impostata?


21

Esiste una struttura di dati che mantenga una raccolta di set (di set di terra finito) a supporto delle seguenti operazioni? Qualche tempo di esecuzione sublineare sarà apprezzato?

  1. Init un set vuoto.
  2. Aggiungi un elemento a un set.
  3. Dato due set, segnala se si intersecano.

1
Questa è una domanda molto generale, poiché qualsiasi struttura di dati può supportare quelle operazioni con dominio finito. Potresti essere un po 'più specifico? Per esempio. Di quale complessità hai bisogno, cosa sei disposto a sacrificare per ottenere operazioni sul set ecc.
Bartosz Przybylski,

Risposte:


13

Se ogni set mantiene un registro di quali altri set esistono e hai un totale di s>0 set, puoi facilmente trasformare qualsiasi struttura di dati per una raccolta ( ad es . Alberi di ricerca binari, ecc. ) In uno in cui puoi recuperare un elemento dell'intersezione di due insiemi nel tempo O(logs) .

  • Ogni set dovrebbe avere un identificatore univoco rispetto a un set totalmente ordinato. Se assegni esplicitamente i tuoi insiemi S1,S2, l'identificatore potrebbe essere solo l'indice.

  • È necessario implementare un "registro" degli insiemi; una struttura di dati che mantiene una raccolta di tutti i set che sono stati definiti. Il registro deve essere implementato come una struttura di dati dell'albero di ricerca, per consentire un facile recupero ( ad esempio  se si desidera eliminare il set) e l'attraversamento lineare dei set.

  • Ogni set Sj mantiene anche un "indice" di ciascuno degli altri set - non una copia di essi, ma piuttosto una struttura di dati che viene indicizzata dalle etichette degli altri set. Questo indice verrà utilizzato per mantenere, per ogni set Sk , un albero di ricerca binario di tutti gli elementi di SjSk . (I due set Sj e Sk condividono una copia di quell'albero di ricerca.)

Inizializzazione

L'inizializzazione di un set consiste in operazioni per inizializzare l'albero dei suoi elementi, operazioni mentre si inizializza (copiando dal registro) l'indice per l'insieme e operazioni mentre attraversi il registro per aggiungere negli indici di ciascuno degli altri insiemi . Nell'indice di , creiamo alberi di ricerca che rappresentano per gli altri insiemi ; copiamo lo stesso puntatore per l'indice di .O ( 1 ) O ( s ) T O ( s log s ) T S j T T S j = T=O(1)O(s)TO(slogs)TSjTTSj=S jSjSj

Aggiunta di un elemento a un setT

L'aggiunta di alcuni all'insieme richiede , dove. Testiamo anche l'appartenenza di in ciascuno degli altri set , che richiede tempo doveè la dimensione dell'universo (o del set più grande ) e è il numero di set nel registro. Per ogni insieme tale che , anche inserto nell'indice per il set . Per ciascuno di questi setT O ( log n T ) n T = | T | x S 1 , S 2 , ... O ( log n S 1 + log n S 2 + ) O ( s log n ) , n = | V | S j s S j x S jxVTO(lognT)nT=|T|xS1,S2,

O(lognS1+lognS2+)O(slogn),
n=|V|SjsSjxSjS jT S j O ( log s + log n T ) S j T x S jT S 1 , S 2 , O ( s log s + s log n T ) S j V s n O ( s log n )xSjTSj, ciò richiede tempo, per cercare nell'indice di e per inserire in ; in tutti i set questo richiede tempo . Se supponiamo che il numero di insiemi sia molto inferiore alla dimensione dell'universo (ovvero, se supponiamo ), il tempo totale per l'inserimento dell'elemento è quindi .O(logs+lognT)SjTxSjTS1,S2,O(slogs+slognT)SjVsnO(slogn)

Se non si consente duplicati in serie, siamo in grado di risparmiare tempo nel caso in cui già rinunciando alla prova di appartenenza e inserimenti per le altre serie . "Inserimento" nel caso in cui sia già presente, richiede solo tempo .T x O ( log n T )xSTxO(lognT)

Test di intersezione

L'indice di ciascun set viene mantenuto precisamente per consentire una rapida valutazione dell'intersezione tra due set e . Per un set , semplicemente controllando il suo indice per il set , non possiamo solo determinare in tempo se interseca o meno , ma possiamo anche recuperare un albero binario contenente l'intero set .S k S j S k O ( log s ) S j S k S jS kSjSkSjSkO(logs)SjSkSjSk

Rimozione dell'elemento

Per eliminare un elemento da un set , lo rimuoviamo non solo dall'albero di ricerca per stesso, ma da ciascuna delle intersezioni per gli insiemi nel suo indice. Questo richiede tempo , dove.T T S jT S j O ( s log n T ) n T = | T |xTTSjTSjO(slognT)nT=|T|

Imposta cancellazione

A causa del sovraccarico di ricerca nel registro, se si hanno molti set, potrebbe essere preferibile eliminare i set quando non sono più necessari. Attraversando l'intero registro, possiamo eliminare dall'indice di tutti gli altri insiemi nel tempo , dominato dal costo di eliminazione dell'albero di ricerca che rappresenta per ciascuno degli altri insiemi , dove.S j O ( s n T ) S jT S j n T = | T |SSjO(snT)SjTSjnT=|T|

Osservazioni

Se si prevede di implementare solo un numero costante di insiemi, i tempi di esecuzione sopra indicati si riducono a:

  • inizializzazione:O(1)

  • inserimento elemento:O(logn)

  • test di intersezione (e recupero dell'intersezione):O(1)

  • rimozione elemento:O(lognT)

  • imposta la cancellazione:O(nS)

dove è la dimensione del set più grande nel registro eper il set cui si sta operando.nnT=|T|T

Se prevedete di avere set , dove è il vostro universo, potreste aver bisogno di una struttura dati diversa se volete che queste operazioni operino in un tempo sub-lineare. Tuttavia, se si hanno coppie di insiemi di cui si sa che non si proveranno mai intersezioni, è possibile ridurre le dimensioni dell'indice per gli insiemi (non includendo insiemi di cui si verificherà l'intersezione) o utilizzare più di un registro ( uno per ogni raccolta di insiemi di cui potresti verificare l'intersezione). In effetti, un registro è utile solo se si desidera un controllo centralizzato per garantire che ogni coppia di insiemi abbia una registrazione reciproca nell'indice: può essere pratico in alcuni casi, all'inizializzazione di una serie , semplicemente registrare l' annuncio hocO(|V|)VSogni nuovo set negli indici degli altri set cui sei interessato all'intersezione conTS


6

Esistono strutture di dati che ti consentono di farlo in tempi non lineari, anche per input nel caso peggiore. Vedere http://research.microsoft.com/pubs/173795/vldb11intersection.pdf (e i riferimenti dei documenti in esso).

Se i tuoi due set S e T hanno una grande intersezione e hai un dizionario per S, cercare elementi di T in ordine casuale dovrebbe darti rapidamente un elemento comune. Il caso più difficile è quando la dimensione dell'intersezione è 0 o 1.


3

Di solito il tuo linguaggio di programmazione preferito supporterà una struttura di dati con elementi unici. In generale ci sono tre approcci popolari: alberi, hash e maschere di bit. Gli elementi dell'albero devono essere comparabili, gli elementi hash devono essere hash e gli elementi maschera di bit devono avere un modo di conversione in numeri interi.

Un set di alberi supporterà l'inserimento in O (registro n) e il test di intersezione nel Caso peggiore O (n registro n).

Un set di hash supporterà l'inserimento in O ammortizzato (1 * h) dove 'h' è il tempo di esecuzione dell'algoritmo di hashing e il test di intersezione nel Caso peggiore O (n).

I set di maschere di bit non sono generalmente usati come set di alberi e hash.


2
Questa sarebbe una risposta decente da Stack Overflow , ma qui vorremmo qualche dettaglio su come e perché funziona.
Raffaello

3

Se il tuo caso consente risposte false positive, utilizzerei Bloom Filter con una singola funzione hash.

Puoi implementarlo come segue:

Init un set vuoto

  • Bnn

Aggiungi un elemento a un set.

  • B[hun'Sh(element)]=1

Dati due insiemi (B1, B2), segnalano se si intersecano.

  • B1 UNND B2 = 0

Complessità

  • nO(1)
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.