2015-07-28 33 views
5

Chcę podzielić nieukończony wykres na osobne, niepołączone ciała. Krawędzie wykresu znajdują się na liście edges.Inny wynik po przetasowaniu listy

Kod podaje inny wynik podczas tasowania kolejności krawędzi. Dlaczego?

from random import shuffle 

edges = [('7', '9'), ('2', '8'), ('4', '10'), ('5', '9'), ('1', '2'), ('1', '6'), ('6', '10')] 
bodylist = [] 
shuffle(edges) 

for edge in edges: 
    #If at least one node of the edge is anywhere in bodylist, append the new nodes to that list. 
    try: 
     index = [i for i, body in enumerate(bodylist) if edge[0] in body or edge[1] in body][0] 
     bodylist[index].append(edge[0]) 
     bodylist[index].append(edge[1]) 
    #If not, make a new list containing the new nodes. 
    except: 
     bodylist.append([edge[0], edge[1]]) 

print([set(x) for x in bodylist]) 

oczekiwany wynik: [{'1', '2', '8', '4', '6', '10'}, {'9', '5', '7'}]

Niektóre rzeczywistych wyjść: [{'9', '5', '7'}, {'1', '2', '8'}, {'10', '4', '6', '1'}]

[{'9', '7', '5'}, {'6', '2', '1', '8'}, {'6', '10', '4'}]

Zauważ, że oczekiwany wynik wychodzi również od czasu do czasu. (Tak powinno być zawsze)

Docenię też różne podejścia, ponieważ ten prawdopodobnie nie jest najlepszy.

+0

Co masz na myśli inną odpowiedź? Różni się od czego? –

+0

Być może nie rozumiem tego ... Generalnie otrzymasz inną odpowiedź, kiedy przetasujesz listę, nie? Czy też mówisz, że tasuje krotki na twojej liście, a nie tylko kolejność krotek? – Stiffo

+0

Wynikiem jest zestaw niepołączonych ciał, a ten zestaw zmienia się za każdym razem, gdy tasuje się krawędzie. Powinien być niezależny od zamówienia. –

Odpowiedz

4

Załóżmy, że masz trzy krawędzie [(1, 2), (3, 4), (2, 3)]. Opisano podłączony wykres.

Jednak twój kod najpierw sprawdzi, czy nie ma (1, 2), dodaj to do bodylist.

Następnie będzie szukał (3, 4), nie znajduj ani 3 ani 4, więc dodaj go jako nową listę.

Wreszcie doda (2, 3) do pierwszej listy, ale nie wróci, aby naprawić swój błąd, nie zda sobie sprawy, że (3, 4) należą do tego samego ciała.

Aby rozwiązać ten problem, można pętla w całości przez pozostałych krawędzi za każdym razem dodać nową krawędzią do organizmu w celu sprawdzenia, czy istnieje związek:

while edges: 
    current_edge = edges.pop(0) 
    body = {current_edge[0], current_edge[1]} 
    i = 0 
    while i < len(edges): 
     if edges[i][0] in body or edges[i][1] in body: 
      body.add(edges[i][0]) 
      body.add(edges[i][1]) 
      edges.pop(i) # Edge added, no need to check it again 
      i = 0 # Restart the loop 
     else: 
      i += 1 
    bodylist.append(body) 

Co szukasz nazywa się connected component wykresu.

Jeśli szukasz skutecznego algorytmu, powinieneś spojrzeć na this answer.

3

Dzieje się tak dlatego, że algorytm jest nieprawidłowy. Problem z twoim algorytmem polega na tym, że zależy on od krawędzi, od których zaczynamy tworzyć listę ciał. Aby to wyjaśnić, weźmy prosty przykład wykresu z 4 krawędziami jako - edges = [('1','2'),('2','3'),('3','4'),('1','4')].

Pierwszy przypadek -

>>> edges = [('1','2'),('2','3'),('3','4'),('1','4')] 
>>> bodylist = [] 
>>> for edge in edges: 
...  #If at least one node of the edge is anywhere in bodylist, append the new nodes to that list. 
...  try: 
...   index = [i for i, body in enumerate(bodylist) if edge[0] in body or edge[1] in body][0] 
...   bodylist[index].append(edge[0]) 
...   bodylist[index].append(edge[1]) 
...  #If not, make a new list containing the new nodes. 
...  except: 
...   bodylist.append([edge[0], edge[1]]) 
... 
>>> print([set(x) for x in bodylist]) 
[{'4', '1', '3', '2'}] 

dostać jedno ciało z wierzchołków - 1, 2, 3, 4. Czemu?

Po rozpoczęciu pracy z (1,2) dodano to do listy karoserii. Potem wziąłeś (2,3), widzisz, że 2 jest już w pozycji dodanej na liście ciał, dodajesz ją ponownie do tej samej i to się dzieje, a wszystkie są dodawane do tego samego ciała.


Teraz możemy przyjmować te same krawędzie w innej kolejności - edges = [('1','2'),('3','4'),('2','3'),('1','4')]. Okazuje się, że -

>>> edges = [('1','2'),('3','4'),('2','3'),('1','4')] 
>>> bodylist = [] 
>>> .... #same logic 
>>> print([set(x) for x in bodylist]) 
[{'4', '1', '3', '2'}, {'4', '3'}] 

Jak widać, tym razem masz dwa różne ciała (i oczywiście nie mają racji) Dlaczego?

Znów zacząłem od (1,2), dodałem to do bodylist jako ciało, następnie wziąłeś (3,4), sprawdzając to, widzisz, że żaden z wierzchołków nie jest już w żadnym ciele, stąd dodałeś to do osobnego ciała. Biorąc trzeci element (2,3), widzimy, że jest on tam zarówno w pierwszym, jak i drugim ciele, ale twoja logika polega po prostu na zabraniu pierwszego ciała i dodaniu do tego elementu. (Czy widzisz gdzie idziesz źle?)


To dlaczego uzyskać różne wyniki, kiedy przetasować listę, a kolejność jest ważna dla logiki (co jest źle).

To, co musisz zrobić, to, że jeśli znajdziesz wierzchołki dla krawędzi w wielu ciałach, musisz scalić oba ciała w jedno ciało.


Kolejna rada, nie trzeba dodawać list do bodylist zamiast możemy użyć sets dla każdego body.

Roztwór próbki będzie wyglądać -

from random import shuffle 

edges = [('7', '9'), ('2', '8'), ('4', '10'), ('5', '9'), ('1', '2'), ('1', '6'), ('6', '10')] 
bodylist = [] 
shuffle(edges) 

for edge in edges: 
    bodies = [body for i, body in enumerate(bodylist) if edge[0] in body or edge[1] in body] 
    if len(bodies) > 0: 
     tempset = {edge[0],edge[1]} 
     for x in bodies: 
      tempset.update(x) 
      print('tempset :',tempset) 
      bodylist.remove(x) 
     bodylist.append(tempset) 
    else: 
     bodylist.append({edge[0],edge[1]}) 

print([set(x) for x in bodylist])