Разработка алгоритма задачи клики
Одно из заданий в моем классе алгоритмов - разработать исчерпывающий алгоритм поиска для решения проблемы клики. То есть, учитывая график размера n, алгоритм должен определить, существует ли полный подграф размера k. Я думаю, что получил ответ, но я не могу не думать, что его можно улучшить. Вот что у меня есть:
Версия 1
input: график, представленный массивом A [0,... n-1], размер k подграфа для поиска.
вывод: True, если существует подграф, False иначе
Алгоритм (в псевдокоде типа python):
def clique(A, k):
P = A x A x A //Cartesian product
for tuple in P:
if connected(tuple):
return true
return false
def connected(tuple):
unconnected = tuple
for vertex in tuple:
for test_vertex in unconnected:
if vertex is linked to test_vertex:
remove test_vertex from unconnected
if unconnected is empty:
return true
else:
return false
Версия 2
input: матрица смежности размером n на n, а k - размер подграфа, чтобы найти
output: все полные подграфы в размера k.
Алгоритм (на этот раз в функциональном псевдокоде Python):
//Base case: return all vertices in a list since each
//one is a 1-clique
def clique(A, 1):
S = new list
for i in range(0 to n-1):
add i to S
return S
//Get a tuple representing all the cliques where
//k = k - 1, then find any cliques for k
def clique(A,k):
C = clique(A, k-1)
S = new list
for tuple in C:
for i in range(0 to n-1):
//make sure the ith vertex is linked to each
//vertex in tuple
for j in tuple:
if A[i,j] != 1:
break
//This means that vertex i makes a clique
if j is the last element:
newtuple = (i | tuple) //make a new tuple with i added
add newtuple to S
//Return the list of k-cliques
return S
Есть ли у кого-нибудь мысли, комментарии или предложения? Это включает ошибки, которые я, возможно, пропустил, а также способы сделать это более читаемым (я не привык к использованию большого псевдокода).
Версия 3
К счастью, я поговорил с моим профессором, прежде чем отправить задание. Когда я показал ему псевдокод, который я написал, он улыбнулся и сказал мне, что я слишком много работал. Во-первых, мне не нужно было вводить псевдокод; Мне просто нужно было продемонстрировать, что я понял проблему. И два, он хотел решения грубой силы. Так что я повернулся, выглядел примерно так:
input: граф G = (V, E), размер клики, чтобы найти k
output: True, если клика существует, false в противном случае
Алгоритм
- Найти декартово произведение V k.
- Для каждого кортежа в результате проверьте, подключена ли каждая вершина ко всем другим. Если все подключены, верните true и выйдите.
- Вернуть false и выйти.
ОБНОВЛЕНИЕ. Добавлена вторая версия. Я думаю, что это становится все лучше, хотя я не добавил никаких фантастических динамических программ (что я знаю).
ОБНОВЛЕНИЕ 2: добавлено несколько комментариев и документации, чтобы сделать версию 2 более удобочитаемой. Вероятно, это версия, в которую я перехожу сегодня. Спасибо за помощь! Хотел бы я принять более одного ответа, но я принял ответ от человека, который помог мне больше всего. Я дам вам понять, что думает мой профессор.
Ответы
Ответ 1
Некоторые комментарии:
- Вам нужно только рассмотреть n-select-k комбинаций вершин, а не все k-кортежи (n ^ k из них).
-
connected(tuple)
выглядит неправильно. Вам не нужно reset unconnected
внутри цикла?
- Как предложили другие, есть лучшие способы грубого принуждения. Рассмотрим следующее рекурсивное соотношение: A (k + 1) -подграф является кликой, если первые k вершин образуют клику, а вершина (k + 1) смежна с каждой из первых k вершин. Вы можете применить это в двух направлениях:
- Начните с 1-клики и постепенно расширяйте клику, пока не получите желаемый размер. Например, если m является наибольшей вершиной в текущей клике, попробуйте добавить вершину {m + 1, m + 2,..., n-1}, чтобы получить клику, которая является одной вершиной больше. (Это похоже на обход дерева по глубине, где дочерние элементы дерева node являются вершинами, большими, чем наибольшая вершина в текущей клике.)
- Начните с подграфа нужного размера и проверьте, является ли это кликой, используя рекурсивное отношение. Настройте таблицу memoization, чтобы сохранить результаты на этом пути.
- (предложение реализации) Используйте матрицу смежности (0-1) для представления ребер в графе.
- (начальная обрезка) Отбросьте все вершины со степенью меньше k.
Ответ 2
Я однажды применил алгоритм, чтобы найти все максимальные клики в графе, что является аналогичной проблемой для вас. То, как я это делал, основывалось на этом документе: http://portal.acm.org/citation.cfm?doid=362342.362367 - он описал решение обратной трассировки, которое я нашел очень полезным в качестве руководства, хотя Я сильно изменил эту статью. Вам понадобится подписка, чтобы справиться с этим, но я предполагаю, что у вашего университета будет один доступный.
Одна вещь в этой статье, хотя я действительно думаю, что они должны были назвать "не установленным" "уже рассмотренным множеством", потому что это просто слишком смущает иначе.
Ответ 3
Алгоритм "для каждого k-набора вершин, если он клик, а затем возвращает true" работает точно. Однако это грубая сила, которая, вероятно, не является тем, что ищет алгоритм. Вместо этого учтите следующее:
- Каждая вершина является 1-кликой.
- Для каждой 1-клики каждая вершина, которая соединяется с вершиной в 1-клике, вносит 2-клику.
- Для каждой 2-клики каждая вершина, которая соединяется с каждой вершиной в 2-клике, вносит вклад в 3-клику.
- ...
- Для каждого (k-1) -клика каждая вершина, которая соединяется с каждой вершиной в (k-1) клике, способствует k-клике.
Эта идея может привести к лучшему подходу.
Ответ 4
Возможно, попробуйте метод динамического программирования.
Ответ 5
Удивительно, что набирать вещи, как вопрос, покажет вам то, что вы только что написали. Эта строка:
P = A x A x A //Cartesian product
должно быть следующим:
P = A k//Декартово произведение
Что вы подразумеваете под A ^ k? Вы принимаете матричный продукт? Если да, то A - матрица смежности (вы сказали, что это массив из n + 1 элементов)?
В нотации setbuilder он будет выглядеть примерно так:
P = {(x0, x1,... xk) | x0 ∈ A и x1 ∈ A... и xk ∈ A}
Это в основном просто декартово произведение A взятых k раз. На бумаге я записал ее как k, являющийся надстрочным индексом A (я только что понял, как это сделать, используя markdown).
Плюс, A - это просто массив каждой отдельной вершины без учета смежности.