Generazione di combinazioni da un insieme di coppie senza ripetizione di elementi


28

Ho un set di coppie. Ogni coppia ha la forma (x, y) in modo tale che x, y appartengano a numeri interi dell'intervallo [0,n).

Quindi, se n è 4, allora ho le seguenti coppie:

(0,1) (0,2) (0,3)
(1,2) (1,3) 
(2,3) 

Ho già le coppie. Ora, devo costruire una combinazione usando n/2coppie in modo tale che nessuno degli interi venga ripetuto (in altre parole, ogni intero appare almeno una volta nella combinazione finale). Di seguito sono riportati gli esempi di una combinazione corretta e errata per una migliore comprensione

 1. (0,1)(1,2) [Invalid as 3 does not occur anywhere]
 2. (0,2)(1,3) [Correct]
 3. (1,3)(0,2) [Same as 2]

Qualcuno può suggerirmi un modo per generare tutte le possibili combinazioni, una volta che ho le coppie.


Forse usando un array 2d per rappresentare le tue coppie. Le combinazioni valide corrispondono a una selezione di n celle di array in modo tale che ogni riga e colonna contenga esattamente 1 cella selezionata.
Joe

4
Stai dicendo che l'ingresso è l'insieme di tutte le coppie? In tal caso, dovresti semplicemente dire che l'ingresso è semplicemente n .
martedì

2
è sempre pari? In caso contrario, le affermazioni "nessuno degli interi viene ripetuto" e "ogni intero appare almeno una volta nella combinazione finale" sono contraddittorie. n
Dmytro Korduban,

1
stesso problema di @rgrig: l'input è tutte coppie non ordinate o è un insieme arbitrario di coppie possibili? Se sono tutte coppie, puoi semplicemente dire che l'input è , non è necessario fornire l'elenco. n
Kaveh

1
Sei interessato a generare tutti gli abbinamenti perfetti del grafico su punti definiti dal tuo set iniziale di coppie. Inoltre sembra che tu consideri quel grafico come il grafico completo su quei punti. La tua domanda sarebbe più chiara se lo menzionassi. Ci sono ( n - 1 ) ! ! : = 1 × 3 × 5 × × ( n - 1 ) tali abbinamenti. n(n1)!!:=1×3×5××(n1)
Marc van Leeuwen,

Risposte:


14

Un modo diretto è una procedura ricorsiva che procede come segue per ogni invocazione. L'input per la procedura è un elenco di coppie che sono già state scelte e un elenco di tutte le coppie.

  1. Calcola il numero più piccolo non già coperto dall'elenco di input. Per la prima invocazione, questo sarà ovviamente 0, perché non sono state scelte coppie.
  2. Se tutti i numeri sono coperti, hai una combinazione corretta, stampalo e ritorna al passaggio precedente. Altrimenti, il numero più piccolo che viene scoperto è l'obiettivo a cui mireremo.
  3. Cerca tra le coppie in cerca di un modo per coprire il numero di destinazione. Se non ce n'è, allora torna al precedente livello di ricorsione.
  4. Se esiste un modo per coprire il numero di destinazione, selezionare il primo modo e chiamare nuovamente in modo ricorsivo l'intera procedura, con la coppia appena selezionata aggiungere all'elenco delle coppie scelte.
  5. Quando ciò ritorna, cerca il modo successivo di coprire il numero di destinazione con una coppia, senza sovrapporre una coppia scelta in precedenza. Se ne trovi uno, selezionalo e chiama nuovamente in modo ricorsivo la procedura successiva.
  6. Continua i passaggi 4 e 5 fino a quando non ci sono più modi per coprire il numero di destinazione. Passa attraverso l'intero elenco di coppie. Quando non ci sono più scelte corrette, tornare al livello precedente della ricorsione.

Il modo per visualizzare questo algoritmo è con un albero i cui percorsi sono sequenze di coppie non sovrapposte. Il primo livello dell'albero contiene tutte le coppie che contengono 0. Per l'esempio sopra, l'albero è

           Radice
             |
     ----------------
     | | |
   (0,1) (0,2) (0,3)
     | | |
   (2,3) (1,3) (1,2)

In questo esempio tutti i percorsi attraverso l'albero forniscono effettivamente raccolte corrette, ma ad esempio se si escludesse la coppia (1,2), il percorso più a destra avrebbe solo un nodo e corrisponderebbe alla ricerca nel passaggio 3 non riuscita.

Algoritmi di ricerca di questo tipo possono essere sviluppati per molti problemi simili di enumerazione di tutti gli oggetti di un tipo particolare.


È stato suggerito che forse l'OP ha significato che tutte le coppie sono nell'input, non solo un insieme di esse come dice la domanda. In tal caso l'algoritmo è molto più semplice perché non è più necessario verificare quali coppie sono consentite. Non è nemmeno necessario generare l'insieme di tutte le coppie; il seguente pseudocodice farà ciò che l'OP ha chiesto. Qui è il numero di input, "list" inizia come un elenco vuoto e "coperto" è un array di lunghezza n inizializzato su 0. Potrebbe essere reso un po 'più efficiente ma non è il mio obiettivo immediato.nn

sub cover {
  i = 0;
  while ( (i < n) && (covered[i] == 1 )) {
   i++;
  }
  if ( i == n ) { print list; return;}
  covered[i] = 1;
  for ( j = 0; j < n; j++ ) {
    if ( covered[j] == 0 ) {
      covered[j] = 1;
      push list, [i,j];
      cover();
      pop list;
      covered[j] = 0;
    }
  }
  covered[i] = 0;
}

Questo dovrebbe funzionare, ma probabilmente non è il modo più efficiente per farlo.
Joe

2
Alla fine, il punto è in qualche modo enumerare i percorsi di quell'albero. Se il numero di coppie nell'elenco di input è molto più piccolo del numero di coppie possibili, questo tipo di algoritmo sarà perfettamente efficiente, in particolare se vengono utilizzate alcune tabelle hash per aiutare a ricordare quali numeri sono già stati coperti in ogni passaggio, in modo che questo può essere interrogato in tempo costante.
Carl Mummert,

Se l'elenco utilizza puntatori, vale la pena dare un'occhiata ai Link danzanti di Knuth . Quando ritorni forma una chiamata ricorsiva e devi ripristinare lo stato precedente dell'elenco.
uli,

10

Sn[0,n)Sn+2Snn

def pairs(n):
    if (n%2==1 or n<2):
        print("no solution")
        return
    if (n==2):
        yield(  [[0,1]]  )
    else:
        Sn_2 = pairs(n-2) 
        for s in Sn_2:
            yield( s + [[n-2,n-1]] )
            for i in range(n/2-1):
                sn = list(s)
                sn.remove(s[i])
                yield( sn + [ [s[i][0], n-2] , [s[i][1], n-1] ] )
                yield( sn + [ [s[i][1], n-2] , [s[i][0], n-1] ] )

Puoi elencare tutte le coppie chiamando

for x in pairs(6):
   print(x)

6

Aggiornamento : la mia risposta precedente riguardava i grafici bipartiti, di cui l'OP NON stava chiedendo. Lo sto lasciando per ora, come informazioni correlate. ma le informazioni più pertinenti si riferiscono a corrispondenze perfette nei grafici non bipartiti.

A questo proposito, c'è un bel sondaggio di Propp che delinea i progressi (fino al 1999). Alcune delle idee contenute in quell'articolo e i relativi collegamenti potrebbero rivelarsi utili. il TL; DR è - è difficile :)

--- Inizio della vecchia risposta

Nota che quello che stai chiedendo di fare è elencare tutti i possibili abbinamenti perfetti su un grafico bipartito. Esistono molti algoritmi diversi per farlo, e in particolare uno dei più recenti è dell'ISAAC 2001 .

L'idea di base è quella di trovare una corrispondenza perfetta utilizzando i flussi di rete e quindi modificarla ripetutamente utilizzando cicli alternati (per ulteriori informazioni, consultare il capitolo del manuale di algoritmi sui flussi di rete).


Il grafico bipartito è costituito dai due insiemi con le etichette date [0, n), e c'è un bordo (i, j) se e solo se (i! = J)
Joe

nKn

2
il permanente calcola la risposta. ma l'OP vuole enumerarli
Suresh

tutti sono isomorfi a causa della struttura del grafico, quindi pensare a applicare le permutazioni potrebbe essere una buona idea (ma il problema è che creerà duplicati).
Kaveh

4

Ogni coppia che scegli elimina due file dalle quali non puoi più scegliere. Questa idea può essere utilizzata per impostare un algoritmo ricorsivo (in Scala):

def combine(pairs : Seq[(Int,Int)]) : Seq[Seq[(Int, Int)]] = pairs match {
  case Seq() => Seq()
  case Seq(p) => Seq(Seq(p))
  case _ => {
    val combinations = pairs map { case (a,b) => {
      val others = combine(pairs filter { case (c,d) =>
        a != c && a != d && b != c && b != d
      })

      others map { s => ((a,b) +: s) }
    }}

    combinations.flatten map { _.sorted } distinct
  }
}

Questo può certamente essere espresso in un modo più efficiente. In particolare, l'idea di non dover considerare intere righe per le combinazioni non viene utilizzata dalla chiamata a filter.


Questo non restituirà anche combinazioni che non includono tutti i numeri, ma che non possono essere estese perché non ci sono coppie nella sequenza originale che possono estenderle? In tal caso, tali combinazioni devono essere filtrate.
Carl Mummert,

n2N

(0,1)n=4

Sì. Ma come ho detto, la mia risposta riguarda lo scenario proposto dall'OP, ovvero non input arbitrari.
Raffaello

Mentre leggo la domanda originale, si tratta di un insieme arbitrario di coppie, l'OP non dice mai che tutte le coppie sono possibili. Ma sono d'accordo che l'OP potrebbe essere più chiaro al riguardo.
Carl Mummert,

4

Mentre ci sono già molti adorabili suggerimenti alla domanda, penso che sarebbe bello sottolineare il trucco di base, generale, dietro di loro.

È molto più facile generare combinazioni uniche se puoi avere un ordinamento totale degli elementi da combinare . In questo modo, l'unicità è garantita se consentiamo solo combinazioni ordinate. Non è nemmeno difficile generare le combinazioni ordinate: basta fare la solita ricerca di enumerazione della forza bruta, ma ad ogni passo selezionare solo elementi più grandi di quelli già selezionati ad ogni passaggio.

L'ulteriore complicazione in questo particolare problema è il desiderio di ottenere solo le combinazioni di lunghezza n / 2 (la lunghezza massima). Questo non è difficile da fare se decidiamo una buona strategia di smistamento. Ad esempio, come sottolineato nella risposta di Carl Mummet, se consideriamo un ordinamento lessicografico (top-down, left-right nel diagramma nella domanda) deriviamo la strategia di prendere sempre l'elemento successivo in modo che la sua prima cifra sia il il numero più piccolo ancora inutilizzato.

Possiamo anche estendere questa strategia se vogliamo generare sequenze di altre lunghezze. Ricorda solo che ogni volta che scegliamo un elemento successivo il cui primo numero non è il più piccolo disponibile, escludiamo che una o più righe di elementi appaiano sulla sottosequenza ordinata, quindi la lunghezza massima della prermutazione diminuisce di conseguenza.


3

Non sono sicuro se questo è ciò che stai chiedendo, ma a quanto ho capito, hai tutti coppie non ordinate di e vuoi contare l'elenco di tutte le coppie che coprire l'insieme dove è un numero pari. Possiamo pensare a questo come ai bordi di , il grafico completo su vertici.(n2)[n]={1,,n}[n]nKnn

Inoltre, la domanda sembra presumere che ciascun numero in compaia una sola volta nell'elenco. Nel qual caso, stiamo solo guardando i rivestimenti che si adattano perfettamente . Il numero di abbinamento in un grafico è uguale al permanente della sua matrice di adiacenza . Quindi dobbiamo calcolare .[n]Perm(Kn)

È noto che il permanente è , ma questo è in generale un caso. Per ci sono tali elenchi. #P-complete Knn!2n2

Il modo più semplice per generare tutti questi è quello di correggere una corrispondenza perfetta e quindi applicare una permutazione di ma questo genererà molti duplicati.[n]

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.