Объединение набора находит алгоритм

У меня есть тысячи строк 1 - 100 чисел, каждая строка определяют группу чисел и отношений среди них. Я должен получить наборы связанных чисел.

Мало Примера: Если у меня есть это 7 строк данных

T1 T2
T3 
T4
T5
T6 T1
T5 T4
T3 T4 T7

Мне нужно не так медленный алгоритм, чтобы знать, что наборы здесь:

T1 T2 T6 (because T1 is related with T2 in the first line and T1 related with T6 in the line 5)
T3 T4 T5 T7 (because T5 is with T4 in line 6 and T3 is with T4 and T7 in line 7)

но то, когда у Вас есть очень большие наборы, мучительно не спешит делать поиск T (x) в каждом большом наборе и делает объединения множеств... и т.д.

У Вас есть подсказка для выполнения в этом не так способ грубой силы?

Я пытаюсь сделать это в Python.

15
задан Dan D. 10 January 2015 в 15:16
поделиться

5 ответов

После того, как вы построили структуру данных, какие именно запросы вы хотите выполнить к ней? Покажите нам свой существующий код. Что такое Т (х)? Вы говорите о «группах чисел», но в ваших примерах данных показаны T1, T2 и т. Д .; пожалуйста, объясни.

Вы читали это: http://en.wikipedia.org/wiki/Disjoint-set_data_structure

Попробуйте взглянуть на эту реализацию Python: http://code.activestate.com/recipes / 215912-union-find-data-structure /

ИЛИ вы можете сами придумать что-нибудь довольно простое и понятное, например

[Обновление: совершенно новый код]

class DisjointSet(object):

    def __init__(self):
        self.leader = {} # maps a member to the group's leader
        self.group = {} # maps a group leader to the group (which is a set)

    def add(self, a, b):
        leadera = self.leader.get(a)
        leaderb = self.leader.get(b)
        if leadera is not None:
            if leaderb is not None:
                if leadera == leaderb: return # nothing to do
                groupa = self.group[leadera]
                groupb = self.group[leaderb]
                if len(groupa) < len(groupb):
                    a, leadera, groupa, b, leaderb, groupb = b, leaderb, groupb, a, leadera, groupa
                groupa |= groupb
                del self.group[leaderb]
                for k in groupb:
                    self.leader[k] = leadera
            else:
                self.group[leadera].add(b)
                self.leader[b] = leadera
        else:
            if leaderb is not None:
                self.group[leaderb].add(a)
                self.leader[a] = leaderb
            else:
                self.leader[a] = self.leader[b] = a
                self.group[a] = set([a, b])

data = """T1 T2
T3 T4
T5 T1
T3 T6
T7 T8
T3 T7
T9 TA
T1 T9"""
# data is chosen to demonstrate each of 5 paths in the code
from pprint import pprint as pp
ds = DisjointSet()
for line in data.splitlines():
    x, y = line.split()
    ds.add(x, y)
    print
    print x, y
    pp(ds.leader)
    pp(ds.group)

и вот результат последнего шага:

T1 T9
{'T1': 'T1',
 'T2': 'T1',
 'T3': 'T3',
 'T4': 'T3',
 'T5': 'T1',
 'T6': 'T3',
 'T7': 'T3',
 'T8': 'T3',
 'T9': 'T1',
 'TA': 'T1'}
{'T1': set(['T1', 'T2', 'T5', 'T9', 'TA']),
 'T3': set(['T3', 'T4', 'T6', 'T7', 'T8'])}
14
ответ дан 1 December 2019 в 02:01
поделиться

Как Jim указал выше, вы по существу ищете связанные компоненты простого неориентированного графа, где узлы - это ваши сущности (T1, T2 и так далее), а ребра представляют парные отношения между ними. Простая реализация поиска связанных компонентов основана на поиске по принципу "сначала в ширину": вы начинаете BFS с первой сущности, находите все связанные с ней сущности, затем начинаете другой BFS с первой еще не найденной сущности и так далее, пока не найдете их все. Простая реализация BFS выглядит так:

class BreadthFirstSearch(object):
    """Breadth-first search implementation using an adjacency list"""

    def __init__(self, adj_list):
        self.adj_list = adj_list

    def run(self, start_vertex):
        """Runs a breadth-first search from the given start vertex and
        yields the visited vertices one by one."""
        queue = deque([start_vertex])
        visited = set([start_vertex])
        adj_list = self.adj_list

        while queue:
            vertex = queue.popleft()
            yield vertex
            unseen_neis = adj_list[vertex]-visited
            visited.update(unseen_neis)
            queue.extend(unseen_neis)

def connected_components(graph):
    seen_vertices = set()
    bfs = BreadthFirstSearch(graph)
    for start_vertex in graph:
        if start_vertex in seen_vertices:
            continue
        component = list(bfs.run(start_vertex))
        yield component
        seen_vertices.update(component)

Здесь adj_list или graph - это структура данных списка смежности, в основном она дает вам соседей данной вершины в графе. Чтобы собрать его из вашего файла, вы можете сделать следующее:

adj_list = defaultdict(set)
for line in open("your_file.txt"):
    parts = line.strip().split()
    v1 = parts.pop(0)
    adj_list[v1].update(parts)
    for v2 in parts:
        adj_list[v2].add(v1)

Затем вы можете выполнить:

components = list(connected_components(adj_list))

Конечно, реализация всего алгоритма на чистом Python, как правило, медленнее, чем реализация на C с более эффективной структурой данных графа. Вы можете рассмотреть возможность использования igraph или другой библиотеки графов, например NetworkX, для выполнения этой работы. Обе библиотеки содержат реализации для поиска связанных компонентов; в igraph это сводится к следующему (при условии, что ваш файл не содержит строк с одиночными записями, принимаются только парные записи):

>>> from igraph import load
>>> graph = load("edge_list.txt", format="ncol", directed=False)
>>> components = graph.clusters()
>>> print graph.vs[components[0]]["name"]
['T1', 'T2', 'T6']
>>> print graph.vs[components[1]]["name"]
['T3', 'T4', 'T5']

Disclaimer: I am one of the authors of igraph

1
ответ дан 1 December 2019 в 02:01
поделиться

Вы можете смоделировать группу, используя набор . В приведенном ниже примере я поместил набор в класс Group, чтобы упростить сохранение ссылок на них и отслеживание некоторого условного элемента «головы».

class Group:
    def __init__(self,head):
        self.members = set()
        self.head = head
        self.add(head)
    def add(self,member):
        self.members.add(member)
    def union(self,other):
        self.members = other.members.union(self.members)

groups = {}

for line in open("sets.dat"):
    line = line.split()
    if len(line) == 0:
        break
    # find the group of the first item on the row
    head = line[0]
    if head not in groups:
        group = Group(head)
        groups[head] = group
    else:
        group = groups[head]
    # for each other item on the row, merge the groups
    for node in line[1:]:
        if node not in groups:
            # its a new node, straight into the group
            group.add(node)
            groups[node] = group
        elif head not in groups[node].members:
            # merge two groups
            new_members = groups[node]
            group.union(new_members)
            for migrate in new_members.members:
                groups[migrate] = group
# list them
for k,v in groups.iteritems():
    if k == v.head:
        print v.members

Вывод:

set(['T6', 'T2', 'T1'])
set(['T4', 'T5', 'T3'])
0
ответ дан 1 December 2019 в 02:01
поделиться

Рассматривайте числа T1, T2 и т.д. как вершины графа. Любые два числа, появляющиеся вместе на линии, соединяются ребром. Тогда ваша задача сводится к поиску всех связных компонентов в этом графе. Вы можете сделать это, начав с точки T1, затем выполнить поиск в ширину или в глубину, чтобы найти все вершины, достижимые из этой точки. Пометьте все эти вершины как принадлежащие классу эквивалентности T1. Затем найдите следующую немаркированную вершину Ti, найдите все еще немаркированные вершины, достижимые из нее, и пометьте их как принадлежащие классу эквивалентности Ti. Продолжайте, пока все вершины не будут помечены.

Для графа с n вершинами и e ребрами этот алгоритм требует O(e) времени и места для построения списков смежности и O(n) времени и места для определения всех связных компонентов. после построения структуры графа.

14
ответ дан 1 December 2019 в 02:01
поделиться

Для достижения этой цели можно использовать структуру данных union find.

Псевдокод такого алгоритма выглядит следующим образом:

func find( var element )
    while ( element is not the root ) element = element's parent
    return element
end func

func union( var setA, var setB )
    var rootA = find( setA ), rootB = find( setB )
    if ( rootA is equal to rootB ) return
    else
        set rootB as rootA's parent
end func

(Взято из http://www.algorithmist.com/index.php/Union_Find)

2
ответ дан 1 December 2019 в 02:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: