Differenza tra set di calcolo tra due set di grandi dimensioni


14

Ho due grandi insiemi di interi UN e B . Ogni set ha circa un milione di voci e ogni voce è un numero intero positivo lungo al massimo 10 cifre.

Qual è l'algoritmo migliore per calcolare e B A ? In altre parole, come posso calcolare in modo efficiente l'elenco delle voci di A che non sono in B e viceversa? Quale sarebbe la migliore struttura di dati per rappresentare questi due set, per rendere efficienti queste operazioni?UNBBUNUNB

L'approccio migliore che posso trovare è quello di memorizzare questi due set come elenchi ordinati e confrontare ogni elemento di con ogni elemento di B , in modo lineare. Possiamo fare di meglio?UNB


Se sei disposto a memorizzarlo in modo diverso, potresti essere in grado di ottenere risultati migliori.
Realz Slaw

Inoltre, se si desidera ottenere i risultati come una struttura di dati implicita; puoi semplicemente creare una struttura del genere che interroga i due set per rispondere a ciascuna delle sue query.
Realz Slaw

1
@ user917279 Un punto importante è: di solito è possibile compensare i tempi di pre-elaborazione / costruzione, tempo di query e utilizzo della memoria. Modifichi la struttura raramente, ma esegui molte query? Viceversa? La memoria è una preoccupazione o no? A tali domande si può rispondere da un punto di vista pratico e informare la scelta del costrutto "teorico" "giusto".
Raffaello

1
@Raphael Suggerisci che si potrebbe fare di meglio degli insiemi confluentemente persistenti (in termini di complessità) usando più memoria e / o dedicando più tempo alla preparazione. Sono solo curioso se pensi che sia possibile. Non vedo le tabelle di ricerca come un'opzione per set di input di queste dimensioni.
smossen

1
@ user917279 Se si considera l'esempio di due enormi insiemi identici, qualsiasi struttura di dati creata usando il consumo di hash supporterebbe il test di uguaglianza in O (1) poiché le strutture uguali verranno unite quando create e quindi condividono la stessa posizione di memoria. Gli insiemi confluentemente persistenti sfruttano il consumo di hash anche quando due strutture sono quasi uguali. La complessità è la migliore che abbia mai visto finora per i set ordinati.
smossen

Risposte:


9

Se si desidera archiviare i set in una struttura di dati specializzata, è possibile ottenere alcune complessità interessanti.

Sia io=O(min(|UN|,|B|,|UNΔB|))

Quindi è possibile impostare le operazioni e A Δ B , ciascuna in O ( I log | A | + | B |UNB,UNB,UNBUNΔBO(iolog|UN|+|B|io) tempo previsto. Quindi, in sostanza, ottieni la dimensione minima dei due insiemi, o, la dimensione della differenza simmetrica, a seconda di quale è minore. Questo è meglio di lineare, se la differenza simmetrica è piccola; vale a dire. se hanno un grande incrocio. In effetti, per le due operazioni di differenza di set desiderate, questo è praticamente sensibile all'output, poiché insieme costituiscono la dimensione della differenza simmetrica.

Vedi Set e mappe confluentemente persistenti di Olle Liljenzin (2013) per ulteriori informazioni.


I brani sul foglio sono ordinati alberi di ricerca. Non li considero come strutture di dati non ordinate.
smossen

@smossen abbastanza vero, l'ho modificato.
Realz Slaw,

6

Una scansione lineare è la migliore che io sappia fare, se gli insiemi sono rappresentati come elenchi collegati ordinati. Il tempo di esecuzione è .O(|UN|+|B|)

Nota che non è necessario confrontare ogni elemento di con ogni elemento di B , a coppie. Ciò porterebbe a un tempo di esecuzione di O ( | A | × | B | ) , che è molto peggio. Invece, per calcolare la differenza simmetrica di questi due set, è possibile utilizzare una tecnica simile all'operazione "unisci" in mergesort, opportunamente modificata per omettere i valori comuni a entrambi i set.UNBO(|UN|×|B|)

Più in dettaglio, è possibile creare un algoritmo ricorsivo come il seguente per calcolare , supponendo che A e B siano rappresentati come elenchi collegati con i loro valori in ordine ordinato:UNBUNB

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:])

L'ho rappresentato in pseudo-Python. Se non leggi Python, A[0]è il capo dell'elenco collegato A, A[1:]è il resto dell'elenco e +rappresenta la concatenazione degli elenchi. Per motivi di efficienza, se stai lavorando in Python, probabilmente non vorrai implementarlo esattamente come sopra - per esempio, potrebbe essere meglio usare i generatori, per evitare di creare molti elenchi temporanei - ma volevo mostrarti le idee nella forma più semplice possibile. Lo scopo di questo pseudo-codice è solo quello di illustrare l'algoritmo, non proporre un'implementazione concreta.

Non penso che sia possibile fare di meglio, se i tuoi set sono rappresentati come elenchi ordinati e vuoi che l'output sia fornito come un elenco ordinato. È fondamentalmente deve guardare ogni elemento di e B . Schizzo informale della giustificazione: se c'è qualche elemento che non hai guardato, non puoi emetterlo, quindi l'unico caso in cui puoi omettere di guardare un elemento è se sai che è presente sia in A che in B , ma come fai a sapere che è presente se non hai osservato il suo valore?UNBUNB


fantastico, abbiamo altre opzioni se il vincolo che i set devono essere memorizzati come elenchi ordinati viene rimosso?
user917279

2

Se A e B sono della stessa dimensione, disgiunti e interlacciati (ad es. Numeri dispari in A e numeri pari in B), probabilmente il confronto a coppie di elementi in tempo lineare è probabilmente ottimale.

Se A e B contengono blocchi di elementi che si trovano esattamente in uno di A o B, o in entrambi, è possibile calcolare la differenza, l'unione e l'intersezione impostate in un tempo sub lineare. Ad esempio, se A e B differiscono esattamente in un elemento, la differenza può essere calcolata in O (log n).

http://arxiv.org/abs/1301.3388


1
Dice che i set sono ordinati, il che potrebbe significare che sono memorizzati come elenchi, alberi di ricerca o qualcos'altro. Se i dati devono essere memorizzati come elenchi, non è interessante chiedere "l'algoritmo migliore per calcolare AB" quando nessun algoritmo potrebbe fare di meglio che scansionare gli elenchi in tempo lineare (per il quale ha già trovato un algoritmo).
smossen

1
Accidenti, hai collegato lo stesso documento di me (io, proprio come te, piuttosto) ... dai un nome ai tuoi collegamenti la prossima volta: D
Realz Slaw

@smossen fantastico, a qualunque conoscenza (?) che ho, li ho rappresentati come elenchi ordinati, ma gradirei con umiltà anche altri suggerimenti.
user917279

2

un'opzione è usare i bitvector per rappresentare i set (dove ilnla posizione rappresenta la presenza o l'assenza di un elemento) e le operazioni di tipo set quindi riducono a operazioni binarie che possono essere eseguite rapidamente (e su più bit in parallelo) su computer digitali. in questo casoUN-B = un'B¯ dove un',Bsono i bitvector. l'efficienza relativa di questa tecnica rispetto ad altre tecniche dipende anche dalla scarsità. per insiemi più densi può essere più efficiente di altri approcci. ovviamente anche l'intera operazione è imbarazzantemente parallela, quindi le operazioni impostate possono essere fatte in parallelo.


Con 1010voci possibili, i vettori di bit non sono affatto pratici.
Raffaello

1
R., manca il punto. un singolo longpuò memorizzare 32 elementi o 1 byte, 8 elementi. quindi le voci 1M possono essere memorizzate solo in ~ 125K RAM! l'archiviazione può essere significativamente più efficiente di altre rappresentazioni a seconda di come viene implementato il problema ...
vzn

Quindi avresti bisogno di oltre 12 MB per i set a cui l'OP è interessato. Questo fa saltare tutte le cache (attualmente) e sarà orribile per i set sparsi. In particolare, la creazione di un set vuoto domina tutte le altre operazioni (per set sparsi). A proposito, Knuth affronta questo problema in TAoCP.
Raffaello

12MB? eh? il poster ha detto che ha solo 2 set. il poster non specificava la scarsità / densità del suo set. questo è indicato nella mia risposta. stai supponendo che abbia set sparsi? non esiste una risposta corretta, l'approccio è indicato come un'opzione alternativa che può essere utile a seconda delle circostanze. non è insolitamente usato in questo contesto ...
vzn

Ti suggerisco di rileggere la domanda: "Ogni set ha circa un milione di voci e ogni voce è un numero intero positivo lungo al massimo 10 cifre". Ci sono1010 numeri diversi che possono verificarsi e ce ne sono circa 106quelli nella lista. Ciò significa che solo lo 0,01% di tutte le voci nel tuo vettore bit sono 1 - lo definirei davvero molto scarso. (Ho scoperto che i miei 12 MB erano troppo bassi; ovviamente è necessario1010B1.15solB.)
Raffaello
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.