让我们考虑一下,有两个数组 I
和 J
决定了邻居对:
I = np.array([0, 0, 1, 2, 2, 3])
J = np.array([1, 2, 0, 0, 3, 2])
这意味着元素 0
有两个邻居 1
和 2
。元素 1
只有 0
作为邻居,依此类推。
创建所有邻居三元组 I'
、J'
、K'
的数组的最有效方法是什么,这样 j
是 i
和 { 的邻居{1}} 是 k
的邻居,条件是 j
、i
和 j
是不同的元素 (k
)?
i != j != k
当然,一种方法是遍历每个元素。有没有更高效的算法? (处理 10-5 亿个元素)
答案 0 :(得分:5)
我会采用一种非常简单的方法并使用熊猫(I
和 J
是您的 numpy 数组):
import pandas as pd
df1 = pd.DataFrame({'I': I, 'J': J})
df2 = df1.rename(columns={'I': 'K', 'J': 'I'})
result = pd.merge(df2, df1, on='I').query('K != J')
优点是 pandas.merge
依赖于非常快速的底层数值实现。此外,您还可以使计算速度更快,例如通过使用索引进行合并。
为了减少这种方法需要的内存,在合并它们之前减小 df1
和 df2
的大小可能非常有用(例如,通过将它们的列的 dtype 更改为适合您的需要)。
以下是如何优化计算速度和内存的示例:
from timeit import timeit
import numpy as np
import pandas as pd
I = np.random.randint(0, 10000, 1000000)
J = np.random.randint(0, 10000, 1000000)
df1_64 = pd.DataFrame({'I': I, 'J': J})
df1_32 = df1_64.astype('int32')
df2_64 = df1_64.rename(columns={'I': 'K', 'J': 'I'})
df2_32 = df1_32.rename(columns={'I': 'K', 'J': 'I'})
timeit(lambda: pd.merge(df2_64, df1_64, on='I').query('K != J'), number=1)
# 18.84
timeit(lambda: pd.merge(df2_32, df1_32, on='I').query('K != J'), number=1)
# 9.28
答案 1 :(得分:1)
没有特别神奇的算法来生成所有的三元组。您可以通过有序搜索避免重新获取节点的邻居,但仅此而已。
a
这有帮助吗?上面的算法还有一些细节需要处理,比如避免重复生成,以及通过派系移动的细节。
答案 2 :(得分:1)
这是使用 networkx(用于图形计算的优化库)对您的问题的初步解决方案:
import numpy as np
import networkx as nx
I = np.array([0, 0, 1, 2, 2, 3])
J = np.array([1, 2, 0, 0, 3, 2])
I_, J_, K_ = [], [], [],
num_nodes = np.max(np.concatenate([I,J])) + 1
A = np.zeros((num_nodes, num_nodes))
A[I,J] = 1
print("Adjacency Matrix:")
print(A)
G = nx.from_numpy_matrix(A)
for i in range(num_nodes):
first_neighbors = list(G.neighbors(i))
for j in first_neighbors:
second_neighbor = list(G.neighbors(j))
second_neighbor_no_circle = list(filter(lambda node: node != i, second_neighbor))
num_second_neighbors = len(second_neighbor_no_circle)
if num_second_neighbors > 0:
I_.extend(num_second_neighbors * [i])
J_.extend(num_second_neighbors * [j])
K_.extend(second_neighbor_no_circle)
I_, J_, K_ = np.array(I_), np.array(J_), np.array(K_)
print("result:")
print(I_)
print(J_)
print(K_)
####### Output #######
Adjacency Matrix:
[[0. 1. 1. 0.]
[1. 0. 0. 0.]
[1. 0. 0. 1.]
[0. 0. 1. 0.]]
result:
[0 1 2 3]
[2 0 0 2]
[3 2 1 0]
我在上面没有打印语句的代码中使用了 %%timeit
来检查运行时间:
49 µs ± 113 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
复杂度分析: 在Depth First Search算法中查找所有邻居的所有邻居本质上需要2个步骤。根据图的拓扑结构,这可能需要 O(|V| + |E|),其中 |E| 是图中的边数,| V|是顶点数。
据我所知,一般图形上没有更好的算法。 但是,如果您确实知道图的一些特殊属性,则运行时间可能会受到更严格的限制,或者可能会根据这些知识改变当前算法。
例如,如果您知道所有顶点至多有 d 条边,并且图有一个连通分量,则此实现的边界变为 O(2d)如果 d << |E| 会更好。
如果您有任何问题,请告诉我。
答案 3 :(得分:1)
您要查找的是图中的 all paths of length 3。您可以使用以下递归算法简单地实现这一点:
import networkx as nx
def findPaths(G,u,n):
"""Returns a list of all paths of length `n` starting at vertex `u`."""
if n==1:
return [[u]]
paths = [[u]+path for neighbor in G.neighbors(u) for path in findPaths(G,neighbor,n-1) if u not in path]
return paths
# Generating graph
vertices = np.unique(I)
edges = list(zip(I,J))
G = nx.Graph()
G.add_edges_from(edges)
# Grabbing all 3-paths
paths = [path for v in vertices for path in findPaths(G,v,3)]
paths
>>> [[0, 2, 3], [1, 0, 2], [2, 0, 1], [3, 2, 0]]