Generowanie kombinacji z zestawu par bez powtarzania elementów


28

Mam zestaw par. Każda para ma taką postać (x, y), że x, y należą do liczb całkowitych z zakresu [0,n).

Jeśli więc n wynosi 4, to mam następujące pary:

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

Mam już pary. Teraz muszę zbudować kombinację za pomocą n/2par, tak aby żadna liczba całkowita nie była powtarzana (innymi słowy, każda liczba całkowita pojawia się co najmniej raz w ostatecznej kombinacji). Poniżej podano przykłady poprawnej i niepoprawnej kombinacji dla lepszego zrozumienia

 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]

Czy ktoś może zasugerować mi sposób na wygenerowanie wszystkich możliwych kombinacji, gdy tylko będę mieć pary.


Być może przy użyciu tablicy 2d do przedstawienia par. Prawidłowe kombinacje odpowiadają wyborowi n komórek macierzy, tak że każdy wiersz i kolumna zawiera dokładnie 1 wybraną komórkę.
Joe

4
Czy mówisz, że dane wejściowe to zbiór wszystkich par? Jeśli tak, to powinieneś powiedzieć, że dane wejściowe to po prostu n .
rgrig

2
Czy zawsze jest parzyste? Jeśli nie, stwierdzenia „żadna z liczb całkowitych się nie powtarza” i „każda liczba całkowita pojawia się co najmniej raz w końcowej kombinacji” są sprzeczne. n
Dmytro Korduban

1
taki sam problem jak @rgrig: czy na wejściu są wszystkie nieuporządkowane pary, czy też jest to dowolny zestaw możliwych par? Jeśli są to wszystkie pary, możesz po prostu powiedzieć, że dane wejściowe to , nie musisz podawać listy. n
Kaveh

1
Jesteś zainteresowany wygenerowaniem wszystkich idealnych dopasowań wykresu dla punktów zdefiniowanych przez twój początkowy zestaw par. Ponadto wydaje się, że bierzesz ten wykres za kompletny wykres w tych punktach. Twoje pytanie byłoby jaśniejsze, gdybyś o tym wspomniał. Są ( n - 1 ) ! ! : = 1 × 3 × 5 × × ( n - 1 ) takie dopasowania. n(n1)!!:=1×3×5××(n1)
Marc van Leeuwen,

Odpowiedzi:


14

Jednym bezpośrednim sposobem jest procedura rekurencyjna, która wykonuje następujące czynności przy każdym wywołaniu. Dane wejściowe do procedury to lista par, które zostały już wybrane, oraz lista wszystkich par.

  1. Oblicz najmniejszą liczbę, która nie jest jeszcze objęta listą wprowadzania. Dla pierwszego wywołania będzie to oczywiście 0, ponieważ nie wybrano żadnych par.
  2. Jeśli wszystkie liczby są uwzględnione, masz poprawną kombinację, wydrukuj ją i zwróć poprzedni krok. W przeciwnym razie najmniejsza liczba, która zostanie odkryta, to cel, do którego będziemy dążyć.
  3. Przeszukuj pary, szukając sposobu na pokrycie liczby docelowej. Jeśli nie ma, to po prostu wróć do poprzedniego poziomu rekurencji.
  4. Jeśli istnieje sposób na zakrycie numeru docelowego, wybierz pierwszy sposób i ponownie wywołaj całą procedurę rekurencyjnie, z wybraną parą dodaj do listy wybranych par.
  5. Kiedy to powróci, poszukaj następnego sposobu na pokrycie liczby docelowej parą, bez nakładania się na wcześniej wybraną parę. Jeśli znajdziesz taki, wybierz go i ponownie uruchom rekurencyjnie następną procedurę.
  6. Kontynuuj kroki 4 i 5, aż nie będzie już więcej sposobów na pokrycie liczby docelowej. Przejrzyj całą listę par. Gdy nie ma już poprawnych wyborów, wróć do poprzedniego poziomu rekurencji.

Sposobem na wizualizację tego algorytmu jest drzewo, którego ścieżki są sekwencjami nienakładających się par. Pierwszy poziom drzewa zawiera wszystkie pary zawierające 0. W powyższym przykładzie drzewo to

           Korzeń
             |
     ----------------
     | | |
   (0,1) (0,2) (0,3)
     | | |
   (2,3) (1,3) (1,2)

W tym przykładzie wszystkie ścieżki przez drzewo faktycznie dają poprawne kolekcje, ale na przykład, jeśli pominiemy parę (1,2), wówczas skrajnie prawa ścieżka będzie miała tylko jeden węzeł i nie powiedzie się wyszukiwanie w kroku 3.

Algorytmy wyszukiwania tego typu można opracować dla wielu podobnych problemów z wyliczaniem wszystkich obiektów określonego typu.


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;
}

To powinno działać, ale prawdopodobnie nie jest to najbardziej efektywny sposób.
Joe,

2
Ostatecznie chodzi o wyliczenie ścieżek tego drzewa. Jeśli liczba par na liście danych wejściowych jest znacznie mniejsza niż liczba możliwych par, ten rodzaj algorytmu będzie doskonale wydajny, szczególnie jeśli niektóre tabele skrótów zostaną użyte, aby zapamiętać, które liczby zostały już uwzględnione na każdym etapie, dzięki czemu można o to pytać w stałym czasie.
Carl Mummert,

Jeśli lista zawiera wskaźniki, to warto zobaczyć Dancing Knuth's Dancing Links . Po powrocie z połączenia rekurencyjnego i konieczności przywrócenia poprzedniego stanu listy.
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] ] )

Możesz wyświetlić listę wszystkich par, dzwoniąc

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

6

Aktualizacja : moja wcześniejsza odpowiedź dotyczyła grafów dwustronnych, o które OP nie pytał. Zostawiam to na razie, jako powiązane informacje. ale bardziej trafne informacje dotyczą idealnych dopasowań w grafach dwudzielnych.

W związku z tym istnieje przyjemna ankieta przeprowadzona przez Propp, która przedstawia postęp (do 1999 r.). Niektóre pomysły zawarte w tym artykule i powiązane linki mogą okazać się przydatne. TL; DR jest - to trudne :)

--- Początek starej odpowiedzi

Zwróć uwagę, że to, o co prosisz, to wyliczyć wszystkie możliwe idealne dopasowania na dwudzielnym wykresie. Istnieje wiele różnych algorytmów do tego, a w szczególności jeden z najnowszych pochodzi z ISAAC 2001 .

Podstawową ideą jest znalezienie jednego idealnego dopasowania za pomocą przepływów sieciowych, a następnie wielokrotne modyfikowanie go za pomocą naprzemiennych cykli (więcej informacji można znaleźć w rozdziale podręcznika na temat algorytmów przepływów sieciowych).


Dwuczęściowy wykres składa się z dwóch zestawów z podanymi etykietami [0, n), i istnieje krawędź (i, j) wtedy i tylko wtedy, gdy (i! = J)
Joe

nKn

2
permanent oblicza odpowiedź. ale OP chce je wymienić
Suresh

wszystkie z nich są izomorficzne ze względu na strukturę grafu, więc myślenie o zastosowaniu permutacji może być dobrym pomysłem (ale problem polega na tym, że stworzy duplikaty).
Kaveh

4

Każda wybrana para eliminuje dwa rzędy, z których nie można już wybierać. Ten pomysł można wykorzystać do skonfigurowania algorytmu rekurencyjnego (w Scali):

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
  }
}

Można to z pewnością wyrazić w bardziej wydajny sposób. W szczególności wezwanie do nie jest używane, aby nie brać pod uwagę całych wierszy dla kombinacji filter.


Czy nie zwróci to również kombinacji, które nie zawierają wszystkich liczb, ale których nie można przedłużyć, ponieważ w oryginalnej sekwencji nie ma par, które mogłyby je przedłużyć? Jeśli tak, te kombinacje należy odfiltrować.
Carl Mummert,

n2N

(0,1)n=4

Tak. Ale jak powiedziałem, moja odpowiedź dotyczy scenariusza zaproponowanego przez PO, tj. Nie arbitralnych danych wejściowych.
Raphael

Kiedy czytam oryginalne pytanie, chodzi o dowolny zestaw par, OP nigdy nie mówi, że wszystkie pary są możliwe. Ale zgadzam się, że PO może być bardziej jasne na ten temat.
Carl Mummert,

4

Chociaż istnieje już wiele uroczych odpowiedzi na pytanie, myślę, że fajnie byłoby wskazać na podstawową, ogólną sztuczkę.

Znacznie łatwiej jest generować unikalne kombinacje, jeśli można uzyskać całkowitą kolejność łączonych elementów . W ten sposób wyjątkowość jest gwarantowana, jeśli pozwolimy tylko na posortowane kombinacje. Nie jest też trudno wygenerować posortowane kombinacje - po prostu przeprowadź zwykłe wyszukiwanie wyliczeń siły brutalnej, ale na każdym kroku wybieraj tylko elementy większe niż te już wybrane na każdym kroku.

Dodatkową komplikacją w tym konkretnym problemie jest chęć uzyskania tylko kombinacji długości n / 2 (maksymalna długość). Nie jest to trudne, jeśli zdecydujemy się na dobrą strategię sortowania. Na przykład, jak wskazano w odpowiedzi Carla Mummeta, jeśli weźmiemy pod uwagę rodzaj leksykograficzny (z góry na dół, lewy-prawy na schemacie w pytaniu), otrzymujemy strategię polegającą na tym, aby zawsze przyjmować następny element, tak aby jego pierwszą cyfrą była najmniejsza wciąż nieużywana liczba.

Możemy również rozszerzyć tę strategię, jeśli chcemy generować sekwencje o innych długościach. Pamiętaj tylko, że za każdym razem, gdy wybieramy kolejny element, którego pierwsza liczba nie jest najmniejszą dostępną, wykluczamy pojawienie się jednego lub większej liczby wierszy elementów na posortowanym podsekwencji, więc maksymalna długość wstępnego cięcia odpowiednio się zmniejsza.


3

Nie jestem pewien, czy o to pytasz, ale jak rozumiem, masz wszystkie nieuporządkowane pary i chcesz policzyć listę wszystkich par, które obejmuje zestaw gdzie jest liczbą parzystą. Można myśleć o tym, jak krawędź pokryć z kompletna wykresu na wierzchołków.(n2)[n]={1,,n}[n]nKnn

Ponadto wydaje się, że pytanie zakłada, że ​​każda liczba w pojawia się tylko raz na liście. W takim przypadku patrzymy tylko na pokrycia, które są idealnie dopasowane . Liczba dopasowań na wykresie jest równa stałej jego macierzy przylegania . Musimy więc obliczyć .[n]Perm(Kn)

Wiadomo, że permanent jest , ale tak jest w ogóle. Dla istnieją takich list. #P-complete Knn!2n2

Najłatwiejszym sposobem wygenerowania tych wszystkich elementów jest poprawienie jednego idealnego dopasowania, a następnie zastosowanie permutacji ale spowoduje to wygenerowanie wielu duplikatów.[n]

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.