Find out if two dictionaries result in a loop

Nicolas :

I have two dicts, where the dict inside the dict stands for the values in the other dict:

dict1 = {1:{1,3}, 2:{2}}
dict2 = {1:{5}, 3:{1}}

I am trying to find out, if the combination would result in a loop, for example:

dict1: 1 -> dict2: 3 -> dict1: 1; This would result in a loop, so my script should throw an error.

dict1: 2 -> not in dict2; Not a loop would be okay

dict1: 1 -> dict2: 1 -> 5 not in dict 1; Not a loop would be okay

Any ideas how I could solve that problem? Thanks in advance!

Itamar Mushkin :

Effectively, your question is about finding a cycle (of any length, if I understand correctly) in a bipartite graph.

With some minor pre-processing, you can use the networkx package to find if there is any cycle in your graph. networkx deals with bipartite graphs, but let's just rename out nodes and not need to use the bipartite trait directly.

To pre-process the dictionaries, we add a prefix 'a' to the nodes in the first dict and a prefix 'b' to the nodes in the second (and not forget to add the prefixes to the edge targets):

dict1 = {'a'+str(k): set(map(lambda x: 'b'+str(x), v)) for k, v in dict1.items()}
dict2 = {'b'+str(k): set(map(lambda x: 'a'+str(x), v)) for k, v in dict2.items()}

Now our dictionaries look like this:

{'a1': {'b3', 'b1'}, 'a2': {'b2'}, 'b1': {'a5'}, 'b3': {'a1'}}
{'b1': {'a5'}, 'b3': {'a1'}}

We merge them together to create a single dictionary:

dict3 = dict1
dict3.update(dict2)

And turn that dictionary into a (directed!) graph:

import networkx as nx
g = nx.DiGraph(dict3)

If you're so inclined, you can draw the network (with nx.draw_networkx(g)) to see what came out: networkx graph

Well, we can see the cycle between 'a1' and 'b3'. To find them automatically - and this is the point of this answer - we can just use the following command:

nx.simple_cycles(g)

which in this case returns a generator object which yields the cycles (in this case, (list(nx.simple_cycles(g)) is [['b3', 'a1']].

But you don't want the whole list; you just want your code to throw an exception if the list is not empty. So, you can use:

assert(not(next(nx.simple_cycles(g), None))

And you're done!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=23610&siteId=1