Domanda

Ecco il jist del problema:Dato un elenco di imposta, come ad esempio:

[ (1,2,3), (5,2,6), (7,8,9), (6,12,13), (21,8,34), (19,20) ]

Restituire un elenco di gruppi di imposta, in modo tale che gli insiemi che hanno un comune elemento sono nello stesso gruppo.

[ [ (1,2,3), (5,2,6), (6,12,13) ], [ (7,8,9), (21,8,34) ], [ (19,20) ] ]

Nota il stickeyness - set (6,12,13) non hanno condiviso l'elemento con (1,2,3), ma vengono messe nello stesso gruppo a causa di (5,2,6).

A complicare le cose, devo dire che non ho questi graziosi set, ma piuttosto una tabella DB con diversi milioni di righe che si presenta come:

element | set_id
----------------
1       | 1
2       | 1
3       | 1
5       | 2
2       | 2
6       | 2

e così via.Così mi piacerebbe un modo per farlo in SQL, ma vorrei essere felice con una direzione generale per la soluzione.

MODIFICA:Cambiato i nomi delle colonne della tabella a (elemento, set_id) invece di (chiave, group_id), per rendere i termini più coerente.Nota che Kev risposta usa il vecchio nomi di colonna.

È stato utile?

Soluzione

Il problema è esattamente il calcolo delle componenti connesse di un hypergraph:i numeri sono i vertici, e il set sono le hyperedges.Un modo consueto di calcolo dei componenti collegati è da alluvioni uno dopo l'altro:

  • per tutti i = 1 to N do:
  • se mi è stata contrassegnata da alcuni j < io, per poi continuare (intendo passare alla prossima mi)
  • altro flood_from(i,i)

dove flood_from(i,j) sono definiti come

  • per ogni insieme S contenente l'ho, se non è già contrassegnati da j quindi:
  • tag S j e per ogni elemento di k di S, se k non è già contrassegnati da j, allora il tag da parte di j, e chiamata flood_from(k,j)

Il tag dell'imposta poi darà i componenti collegati che stai cercando.


In termini di banche dati, l'algoritmo può essere espresso come segue:si aggiunge un TAG colonna del database, e si calcola il componente collegato di impostare i lavoretti

  • S = selezionare tutte le righe in cui set_id = = = = = i
  • set di TAG per i per le righe in S
  • S' = selezionare tutte le righe in cui il TAG non viene impostato e il cui elemento è l'elemento(S)
  • mentre S' non è vuoto, non
  • ---- set di TAG per i per le righe in S'
  • ---- S" = selezionare tutte le righe in cui il TAG non viene impostato e il cui elemento è l'elemento(S')
  • ---- S = S union S'
  • ---- S' = S"
  • ritorno set_id(S)

Altro (teorico) modo di presentare questo algoritmo, sarebbe a dire che siete in cerca di punti fissi di un mapping:

  • se A = {A1, ..., Unn} è un insieme di insiemi, definire unione(A) = A1 unione ...unionen
  • se K = {k1, ..., kp} è un insieme di numeri interi, definire incidenze(K) = l'insieme degli insiemi che si intersecano K

Quindi, se S è un insieme, il componente collegato di S è ottenuto mediante iterazione (incidenze)o(unione) in S fino a un punto fisso è raggiunto:

  1. K = S
  2. K' = incidenza(unione(K)).
  3. se K == K', per poi tornare a K altro K = K' e vai a 2.

Altri suggerimenti

Si potrebbe pensare ad esso come un grafico problema in cui il set (1,2,3) è collegato al set (5,2,6) via 2.E quindi utilizzare un algoritmo standard per bene collegato sub-grafici.

Ecco una rapida implementazione di python:

nodes = [ [1,2,3], [2,4,5], [6,7,8], [10,11,12], [7,10,13], [12], [] ]
links = [ set() for x in nodes ]

#first find the links
for n in range(len(nodes)):
    for item in nodes[n]:
        for m in range(n+1, len(nodes)):
            if (item in nodes[m]):
                links[n].add(m)
                links[m].add(n)

sets = []
nodes_not_in_a_set = range(len(nodes))

while len(nodes_not_in_a_set) > 0:
    nodes_to_explore = [nodes_not_in_a_set.pop()]
    current_set = set()
    while len(nodes_to_explore) > 0:
        current_node = nodes_to_explore.pop()
        current_set.add(current_node)
        if current_node in nodes_not_in_a_set:
            nodes_not_in_a_set.remove(current_node)
        for l in links[current_node]:
            if l not in current_set and l not in nodes_to_explore:
                nodes_to_explore.append(l)
    if len(current_set) > 0:
        sets.append(current_set)

for s in sets:
    print [nodes[n] for n in s]

output:

[[]]
[[6, 7, 8], [10, 11, 12], [7, 10, 13], [12]]
[[1, 2, 3], [2, 4, 5]]

Questo è probabilmente piuttosto inefficiente, ma dovrebbe funzionare, almeno:Iniziare con un tasto, selezionare tutti i gruppi che lo contengono tasto, selezionare tutte le chiavi di quei gruppi, selezionare tutte le categorie che contengono le chiavi, ecc.... e non appena un passo che non aggiunge nuove chiavi o gruppi, si dispone di un elenco di tutti i gruppi di una sub-grafico.Escludere tali e ripetere dall'inizio fino a quando non si hanno dati a sinistra.

In termini di SQL questo avrebbe bisogno di una stored procedure, penso.CON RICORSIVA potrebbe aiutare in qualche modo, ma non ho alcuna esperienza con esso ancora, e io non sono sicuro disponibile sul vostro DB backend.

Dopo aver pensato a questo un po ' di più, mi è uscito questo:

  1. Creare una tabella denominata groups con colonne (group_id, set_id)
  2. Ordinare l' sets tabella element.Ora dovrebbe essere facile da trovare elementi duplicati.
  3. Scorrere il set di tavolo, e quando si trova un elemento duplicato fare:
    1. se uno dei set_id campi esiste in groups tabella, aggiungere l'altra con la stessa group_id.
    2. Se non set_id esiste in groups tabella, generare un nuovo ID di gruppo e aggiungere sia set_ids groups tabella.

Alla fine dovrei avere un groups tabella contenente tutti i set.

Questo non è puro SQL, ma sembra che O(nlogn), quindi credo che posso vivere con questo.

Matt risposta sembra più corretto, ma non so come implementarlo nel mio caso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top