在python中使用networkx计算无向图中大小为k的集团的最佳方法是什么?

Gol*_*bug 3 python graph-theory clique-problem networkx

我很惊讶 networkx 似乎没有内置函数来做到这一点,但也许我错过了一些使用内置算法来做到这一点的聪明方法?

Mic*_*nko 5

您可以使用以下内置函数之一:enumerate_all_cliquesfind_cliques以获取无向图中的所有 k-clique。

这些函数之间的区别是enumerate_all_cliques遍历所有可能的派系而find_cliques只遍历最大的派系。我们最终会看到它会影响运行时间。

选项 1 使用enumerate_all_cliques

import networkx as nx

def enumerate_all_cliques_size_k(G, k):
    i = 0
    for clique in nx.enumerate_all_cliques(G):
        if len(clique) == k:
            i += 1
        elif len(clique) > k:
            return i
    return i
Run Code Online (Sandbox Code Playgroud)

选项 2 使用find_cliques

import networkx as nx
import itertools

def find_cliques_size_k(G, k):
    i = 0
    for clique in nx.find_cliques(G):
        if len(clique) == k:
            i += 1
        elif len(clique) > k:
            i += len(list(itertools.combinations(clique, k)))
    return i

Run Code Online (Sandbox Code Playgroud)

第一个选项更直接,但它的运行时间是有问题的,因为我们遍历了最大集团的所有可能子集,即使最大集团规模小于 k。我们可以看到enumerate_all_cliques_size_k在大小为 20 的完整图上运行需要 10 倍的时间:

G = nx.complete_graph(20)


@timing
def test_enumerate_all_cliques_size_k(G,k):
    print(enumerate_all_cliques_size_k(G, k))

@timing
def test_find_cliques_size_k(G, k):
    print(find_cliques_size_k(G, k))

test_enumerate_all_cliques_size_k(G,5)
test_find_cliques_size_k(G,5)

# --------------------Result-----------------------

15504
test_enumerate_all_cliques_size_k function took 616.645 ms
15504
test_find_cliques_size_k function took 56.967 ms
Run Code Online (Sandbox Code Playgroud)


小智 5

使用 find_cliques 函数时,在检查所有可能性时需要小心(itertools.combinations) - 在某些情况下,您会多次计算同一个 clique。例如,如果您有一个包含六个节点的图(让我们将它们命名为 AG)。其中四个是全连接(AD),E与AD连接,G也与AD连接(但E不与G连接)。在这种情况下,您有两个共享 4 个节点的 5 派(A、B、C、D、E 和 A、B、C、D、G)。现在假设您正在这个建议的 garph 中寻找 4-cliques,通过使用 find_cliques,您将遍历两个 5-cliques,并且在每个 5-cliques 中,您将计算每个 4-cliques,其中包括 4-clique A ,B,C,D,所以会被计算两次(!)。

这里是建议函数的一个版本,它通过使用 set 来解决这个问题,这样你就可以只对每个 clique 计数一次:

def find_cliques_size_k(G, k):
    all_cliques = set()
    for clique in nx.find_cliques(G):
        if len(clique) == k:
            all_cliques.add(tuple(sorted(clique)))
        elif len(clique) > k:
            for mini_clique in itertools.combinations(clique, k):
                all_cliques.add(tuple(sorted(mini_clique)))
    return len(all_cliques)
Run Code Online (Sandbox Code Playgroud)

(如果您想要派系本身,您可以返回“all_cliques”本身)