Подсчет подключенных компонентов - PullRequest
0 голосов
/ 25 ноября 2011

В стандартном алгоритме для подсчета подключенных компонентов используется структура данных с несвязанным множеством, называемая union-find .

Почему используется эта структура данных?Я написал код, чтобы просто искать изображение линейно, поддерживая два линейных буфера для хранения текущего и следующего количества компонентов для каждого подключенного пикселя, просто исследуя четырех соседей (E, SE, S, SW), и в случае соединения,обновите карту соединений, чтобы объединить более высокий компонент с более низким компонентом.После этого найдите все несоединенные компоненты и сообщите количество.

Я просто не понимаю, почему этот подход менее эффективен, чем использование union-find.

Вот мой код.Входной файл был уменьшен до 0 с и 1 с.Программа выводит количество подключенных компонентов, сформированное из 0 с.

def CompCount(fname):
    fin = open(fname)
    b,l = fin.readline().split()
    b,l = int(b),int(l)+1
    inbuf = '1'*l + fin.read()
    prev = curr = [sys.maxint]*l
    nextComp = 0
    tree = dict()
    for i in xrange(1, b+1):
        curr = [sys.maxint]*l
        for j in xrange(0, l-1):
            curr[j] = sys.maxint
            if inbuf[i*l+j] == '0':
                p = [prev[j+n] for m,n in [(-l+1,1),(-l,0),(-l-1,-1)] if inbuf[i*l + j+m] == '0']
                curr[j] = min([curr[j]] + p + [curr[j-1]])
                if curr[j] == sys.maxint:
                    nextComp += 1
                    curr[j] = nextComp
                    tree[curr[j]] = 0                   
                else:
                    if curr[j] < prev[j+1]: tree[prev[j+1]] = curr[j]
                    if curr[j] < prev[j]:   tree[prev[j]]   = curr[j]
                    if curr[j] < prev[j-1]: tree[prev[j-1]] = curr[j]
                    if curr[j] < curr[j-1]: tree[curr[j-1]] = curr[j]
        prev = curr
    return len([x for x in tree if tree[x]==0])

Ответы [ 3 ]

0 голосов
/ 30 ноября 2011

Ваш алгоритм имеет недостатки.Рассмотрим следующий пример:

11110
01010
10010
11101

Ваш алгоритм содержит 2 компонента, тогда как он имеет только 1.

Для тестирования я использовал эту слегка измененную версию вашего кода.

import sys

def CompCount(image):
    l = len(image[0])
    b = len(image)
    prev = curr = [sys.maxint]*(l+1)
    nextComp = 0
    tree = dict()
    for i in xrange(b):
        curr = [sys.maxint]*(l+1)
        for j in xrange(l):
            curr[j] = sys.maxint
            if image[i][j] == '0':
                p = [prev[j+n] for m,n in [(1,1),(-1,0),(-1,-1)] if 0<=i+m<b and 0<=j+n<l and image[i+m][j+n] == '0']
                curr[j] = min([curr[j]] + p + [curr[j-1]])
                if curr[j] == sys.maxint:
                    nextComp += 1
                    curr[j] = nextComp
                    tree[curr[j]] = 0                   
                else:
                    if curr[j] < prev[j+1]: tree[prev[j+1]] = curr[j]
                    if curr[j] < prev[j]:   tree[prev[j]]   = curr[j]
                    if curr[j] < prev[j-1]: tree[prev[j-1]] = curr[j]
                    if curr[j] < curr[j-1]: tree[curr[j-1]] = curr[j]
        prev = curr
    return len([x for x in tree if tree[x]==0])

print CompCount(['11110', '01010', '10010', '11101'])

Позвольте мне попытаться объяснить ваш алгоритм словами (с точки зрения графика, а не сетки).

  • Установить «корни» как пустое множество.
  • Итерировать поузлы на графике.
    • Для узла n посмотрите на все его соседи, уже обработанные.Назовите этот набор A.
    • Если A пусто, выберите новое значение k, установите v [узел] равным k, и добавьте k к корням.
    • В противном случае, пусть k будет минимальнымv [узел] для узла в A. Удалите v [x] из корней для каждого x в A с v [x]! = k.
  • Число компонентов - это числоэлементов корней.

(Ваш tree совпадает с моим roots: обратите внимание, что вы никогда не используете значение tree[] элементов, только если они равны 0 или нет ..это просто реализация набора)

Это похоже на union-find, за исключением того, что предполагается, что при объединении двух компонентов тот, который имеет более высокое значение v [], никогда ранее не объединялся с другим компонентом.В контрпримере это используется, потому что два 0 в центральном столбце были объединены с 0 слева.

0 голосов
/ 17 декабря 2011

Мой вариант:

  1. Разбейте весь график на ребра.Добавьте каждое ребро в набор.
  2. На следующей итерации нарисуйте ребра между 2 внешними узлами ребра, созданного на шаге 2. Это означает добавление новых узлов (с их соответствующими наборами) к набору исходного ребрабыл из.(в основном устанавливается слияние)
  3. Повторяйте 2, пока 2 искомых узла не окажутся в одном наборе.Вам также нужно будет выполнить проверку после шага 1 (на всякий случай, если 2 узла смежны).

Сначала каждый из ваших узлов будет в своем наборе,

 o-o     o-o    o1-o3  o2   o3-o4
   \     /                    |
   o-o-o-o             o2  o1-o3-o4 

По мере того, как алгоритм прогрессирует и объединяет множества, он относительно вдвое сокращает входные данные.

В примере я проверяю компоненты в некотором графе.После слияния всех ребер до их максимально возможного набора у меня осталось 3 набора, дающих 3 отсоединенных компонента.(Количество компонентов - это количество наборов, которое вы можете получить после завершения работы алгоритма.)

Возможный график (для дерева выше):

o-o-o  o4  o2
  |    |
  o    o3
       |
       o1
0 голосов
/ 25 ноября 2011

Я не совсем понял ваш вопрос, вы действительно выиграете для себя, если четко сформулируете его и структурируете свой вопрос.

Что я понимаю, так это то, что вы хотите сделать маркировку подключенного компонента на изображении 0-1, используя окрестность 8. Если это так, то ваше предположение о том, что результирующий граф окрестностей будет плоским, неверно. У вас есть переходы на «диагонали». В таком изображении должно быть легко построить K_ {3,3} или K_ {5}.

...