简体   繁体   中英

Convert a list of weighted edges into an adjacency matrix in a memory-efficient way

Using Python, how can I convert a list of weighted edges into a symmetric adjacency matrix in a memory-efficient way?

For example, consider the following list of weighted edges containing 4 nodes and 3 edges:

A B 1
A C 2
C D 3

Then the output adjacency matrix is the following:

0 1 2 0
1 0 0 0
2 0 0 3
0 0 3 0

I am looking to do this in a memory-efficient way—100000 by 100000 adjacency matrix (10**10 values). Note that the matrix is symmetric and all of the diagonal values are 0.

You can use a sparse matrix. To make it more accessible, use a dictionary for sparse matrix. Use tuple of adjacent vertices as key to the dictionary.

nodes = ['A','B','C','D']
graph = {('A','B'):1, ('A','C'): 2, ('C','D'): 3}

And to access the nodes we use two nested for loops. For each pair of nodes we can check if an edge is present in O(1) time complexity. For example:

for i in nodes:
    for j in nodes:
        if (i,j) in graph:
            print(i,j,graph[(i,j)])
        else:
            print("Edge not present.")

Even if you use characters to denote the nodes, you can use the node list in This method has same time complexity as the regular matrix method. But the space complexity is reduced extremely. And adding new edges or removing edges from the graph dictionary is very easy.

You could use a dictionary of dictionaries and only store the edges once with their weight. Using functions to access the weights will allow you to invert the parameter order as needed to get and set the weight values.

def getWeight(adj,a,b):
    return getWeight(adj,b,a) if a>b else adj.get(a,{}).get(b,0)

def setWeight(adj,a,b,w):
    if a>b: setWeight(adj,b,a,w)
    else:   adj.setdefault(a,{})[b] = w
    
edges  = [('A','B',1),('A','C',2),('C','D',3)]
adj    = {v:{} for e in edges for v in e[:2]}

for a,b,w in edges:
    setWeight(adj,a,b,w)

Output:

print(adj)
{'A': {'B': 1, 'C': 2}, 'B': {}, 'C': {'D': 3}, 'D': {}}

print(getWeight(adj,'B','A')) # 1
print(getWeight(adj,'A','B')) # 1
print(getWeight(adj,'D','A')) # 0

If you have the vertices somewhere in another list, you only need to store the edge weights in the dictionary which will make it slightly more memory efficient:

def getWeight(adj,a,b):
    return adj[a,b] if (a,b) in adj else adj.get((b,a),0)

def setWeight(adj,a,b,w):
    if (b,a) in adj: adj[b,a] = w
    else:            adj[a,b] = w
    
edges  = [('A','B',1),('A','C',2),('C','D',3)]
adj    = {(a,b):w for a,b,w in edges}

print(adj)
{('A', 'B'): 1, ('A', 'C'): 2, ('C', 'D'): 3}

print(getWeight(adj,'B','A')) # 1
print(getWeight(adj,'A','B')) # 1
print(getWeight(adj,'D','A')) # 0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM