【问题标题】:More elegant way to generate adjacency matrix from list of tuples从元组列表生成邻接矩阵的更优雅的方法
【发布时间】:2019-05-14 16:31:24
【问题描述】:

假设我们从一个由元组列表表示的“友谊”图开始,

friendships = [(0, 1), (0, 2), (1, 2), (1, 3), (2,
3), (3, 4),(4, 5), (5, 6), (5, 7), (6, 8), (7, 8), (8, 9)]

其中元素 0 是 1 的朋友(因此 1 是 0 的朋友)。

我想从头开始以一种始终适用于这种类型的元组表示的方式构造邻接矩阵。

我有以下(令人厌恶的)Python 代码:

def make_matrix(num_rows,num_cols,entry_fn):
    return [[entry_fn(i,j)
             for j in range(num_cols)]
            for i in range(num_rows)]
def adjacency(connections):
    new=connections+[(x[1],x[0]) for x in connections]
    elements=list(set([x[0] for x in connections]+ [x[1] for x in connections]))
    def test(i,j):
        if (elements[i],elements[j]) in new:
            return 1
        else: return 0
    return make_matrix(len(elements),len(elements),test)

我知道它效率低下而且非常丑陋。有没有更聪明的方法来解决这个问题?我上面给出的示例列表的输出应该是

[[0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
 [1, 0, 1, 1, 0, 0, 0, 0, 0, 0],
 [1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
 [0, 1, 1, 0, 1, 0, 0, 0, 0, 0],
 [0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
 [0, 0, 0, 0, 1, 0, 1, 1, 0, 0],
 [0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
 [0, 0, 0, 0, 0, 0, 0, 0, 1, 0]]

更新: 根据其中一个答案,我有以下可能的解决方案,虽然我不知道它是否更好

def adj(connections):
    ##step 1
    temp=(set(elem[0] for elem in connections).union(
        set(elem[1] for elem in connections)))
    n=max(temp)+1
    ans=[]
    ##step 2
    for i,_ in enumerate(temp):
        ans.append([])
        for j,_ in enumerate(temp):
            ans[i].append(0)
    ##step 3
    for pair in connections:
        ans[pair[0]][pair[1]]=1
        ans[pair[1]][pair[0]]=1
    return ans

【问题讨论】:

  • 你提前知道顶点的数量吗?
  • @Code-Apprentice no,这是挑战的一部分,也是我在中间有这个粗略元素定义的原因
  • 从头开始 - 这是否意味着您实际上只在寻找原生解决方案而库不在?
  • @ParitoshSingh 是的。我对具有可以调用的邻接函数的图形/网络库不感兴趣。

标签: python function graph adjacency-matrix


【解决方案1】:

我的算法是这样的:

  1. 找到最大的顶点id。打电话给n
  2. 通过n+1 创建一个n+1 零数组。打电话给M
  3. 对于输入列表中的每一对x, y,设置M[x][y] = 1

为了提出这个解决方案,我首先想到了第 3 步。对于给定的输入,这似乎是填充邻接矩阵的最直接的方法。但是,它需要一个固定大小的二维数组。所以问题是我如何找出第 2 步的n。从那里开始,不需要太多思考就可以确定第 1 步是需要的。

细节留给读者练习。

【讨论】:

  • n 应该比最大 ID 大一
  • @TavianBarnes 或者,数组的维度应该比 n 大一。
  • @AndresMejia 是的,该代码看起来非常接近地翻译了我的伪代码。然而,逻辑上有一个缺陷。例如,如果connections[(1, 2), (5, 3)],那么n 应该是6。我把它留作练习,让你找出问题所在。您可能会发现 this article 很有帮助。
  • @AndresMejia “我仍然有这种挥之不去的怀疑,认为它没有想象中的那么好”无论你编码多长时间,这种感觉都会困扰你。我们只是随着经验的积累而改进,有时“足够好”就足够了。
  • @Code-Apprentice 好吧,也许你是对的。我会继续前进:)
【解决方案2】:

我不是一个 Python 程序员,但有点像......

def to_adjacency_matrix(pairs):
  size = 1 + max(map(max, pairs))
  a = [[0 for j in range(size)] for i in range(size)]
  for i, j in pairs:
    a[i][j] = a[j][i] = 1
  return a

【讨论】:

  • 另外,您对size 的计算并不完全正确。例如,如果pairs = [(1, 10), (5, 2)],那么大小将被分配6,而不是11 的正确值。注意reduce(max, foo) 等价于max(foo)
  • 提示:为了初始化a,您可以使用* 运算符简化内部列表理解。 a = [[0] * size] for i in range(size)].
  • @Code-Apprentice 很抱歉在思考map 时输入了reduce
  • @Code-Apprentice 实际上我最初写了[[0] * size],然后更改了它。您不能说[[0] * size] * size,因为这是对同一数组的引用数组。所以上面的方法看起来更简单。
【解决方案3】:

如果您想要简短易读的解决方案(并且您计划稍后使用图形),我建议您使用 networkx 库。你的问题可以用一对线来解决:

import networkx as nx
G = nx.Graph()
G.add_edges_from(friendships)
nx.to_numpy_matrix(G)

这将返回 numpy 矩阵:

matrix([[0., 1., 1., 0., 0., 0., 0., 0., 0., 0.],
        [1., 0., 1., 1., 0., 0., 0., 0., 0., 0.],
        [1., 1., 0., 1., 0., 0., 0., 0., 0., 0.],
        [0., 1., 1., 0., 1., 0., 0., 0., 0., 0.],
        [0., 0., 0., 1., 0., 1., 0., 0., 0., 0.],
        [0., 0., 0., 0., 1., 0., 1., 1., 0., 0.],
        [0., 0., 0., 0., 0., 1., 0., 0., 1., 0.],
        [0., 0., 0., 0., 0., 1., 0., 0., 1., 0.],
        [0., 0., 0., 0., 0., 0., 1., 1., 0., 1.],
        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.]])

nx.adjacency_matrix(G)

这将返回 scipy 稀疏矩阵:

<10x10 sparse matrix of type '<class 'numpy.int64'>'
    with 24 stored elements in Compressed Sparse Row format>

【讨论】:

    【解决方案4】:

    我愿意:

    import numpy as np
    
    def adjacency(connections):
        n_people = max(sum(connections, ())) + 1
        mat = np.zeros((n_people, n_people), dtype='int')
        for friend1, friend2 in connections:
            mat[friend1, friend2] = 1
            mat[friend2, friend1] = 1
        return mat
    

    如果“从头开始”你的意思是你不想使用 numpy:

    def adjacency(connections):
        n_people = max(sum(connections, ())) + 1
        mat = [[0 for _ in range(n_people)] for _ in range(n_people)]
        for friend1, friend2 in connections:
            mat[friend1][friend2] = 1
            mat[friend2][friend1] = 1
        return mat
    

    【讨论】:

      猜你喜欢
      • 2014-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-31
      相关资源
      最近更新 更多