J - 19 15 char
J doesn't have a dictionary type, and is generally garbage with representing things the way people are used to in other languages. This is a bit idiosyncratic with how it represents things, but that's because J and graphs don't blend well, and not because I'm looking for optimal ways of representing directed graphs. But even if it doesn't count, I think it's a concise, interesting algorithm.
~.@(],;@:{~)^:_
This is a function which takes a graph on the left and a list of nodes to start with on the right.
Graphs are lists of boxed lists, and the nodes are indices into the list that represents the graph. J indexes from zero, so nodes are nonnegative integers, but you can skip 0 and pretend to have 1-based indexing by using an empty list (''
) in the 0 place. (Indented lines are input to the J console, lines flush with the margin are output.)
2 4;1 2 4;0;0 2;1 NB. {0: [2, 4], 1: [1, 2, 4], 2: [0], 3: [0, 2], 4:[1]}
+---+-----+-+---+-+
|2 4|1 2 4|0|0 2|1|
+---+-----+-+---+-+
'';2 3;3;1;2 NB. {1: [2, 3], 2: [3], 3: [1], 4:[2]}
++---+-+-+-+
||2 3|3|1|2|
++---+-+-+-+
'';'';2;9;1 7 8 9;3 6;4 8;7;'';3;'';7 NB. the big Mathematica example
+++-+-+-------+---+---+-++-++-+
|||2|9|1 7 8 9|3 6|4 8|7||3||7|
+++-+-+-------+---+---+-++-++-+
Lists of numbers are just space-separated lists of numbers. In general a scalar (just one number in a row) is not the same as a one-item list, but I use only safe operations that end up promoting scalars to one-item lists, so everything's cool.
The function explained (supposing we invoke as graph ~.@(],;@:{~)^:_ nodes
):
{~
- For each node in nodes
, take the list of adjacent nodes from graph
.
;@:
- Turn the list of adjacencies into a list of nodes.
],
- Prepend nodes
to this list.
~.@
- Throw out any duplicates.
^:_
- Repeat this procedure with the same graph
until it results in no change to the right hand argument, i.e. there are no new nodes to discover.
The verb in action:
('';2 3;3;1;2) ~.@(],;@:{~)^:_ (1) NB. example in question
1 2 3
f =: ~.@(],;@:{~)^:_ NB. name for convenience
(2 4;1 2 4;0;0 2;1) f 2
2 0 4 1
('';'';2;9;1 7 8 9;3 6;4 8;7;'';3;'';7) f 4 NB. Mathematica example
4 1 7 8 9 3
If you don't like this graph representation, here's a couple quick verbs for converting to/from adjacency matrices.
a=:<@#i.@# NB. adj.mat. to graph
mat=:0,0,.0 1 1 0,0 0 1 0,1 0 0 0,:0 1 0 0
mat NB. adj.mat. for {1:[2,3],2:[3],3:[1],4:[2]}
0 0 0 0 0
0 0 1 1 0
0 0 0 1 0
0 1 0 0 0
0 0 1 0 0
a mat
++---+-+-+-+
||2 3|3|1|2|
++---+-+-+-+
A=:+/@:=&>/i.@# NB. graph to adj.mat.
A a mat
0 0 0 0 0
0 0 1 1 0
0 0 0 1 0
0 1 0 0 0
0 0 1 0 0
For anyone wondering why the graph is the left argument to the verb, it's because that's the general convention in J: control information on the left, data on the right. Consider list search, list i. item
, and the sequential machine dyad, machine ;: input
.
Not sure. Methinks it's
m = {{0, 1, 1, 0}, {1, 0, 1, 1}, {1, 1, 0, 1}, {0, 1, 1, 0}}; ConnectedComponents[AdjacencyGraph[m, DirectedEdges -> True], 1]
– Dr. belisarius – 2014-04-20T06:18:14.657I'll check and get back to you. – DavidC – 2014-04-20T12:28:29.423
Nope. Mine is wrong. Not sure about yours. – Dr. belisarius – 2014-04-20T15:48:17.230
This seems to work:
m = {{0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 1, 0}}; VertexOutComponent[AdjacencyGraph@m, 3, Infinity]
. TheDirectedEdges -> True
isn't needed. – Dr. belisarius – 2014-04-20T15:55:36.520I forgot about
VertexOutComponent
. Would you like to post? – DavidC – 2014-04-20T20:24:35.6471Nah, I think one Mma answer per Q is enough. Please be my guest to use the code if that fits you :) – Dr. belisarius – 2014-04-20T20:27:16.603
Is the
∞
here necessary? – alephalpha – 2014-04-21T16:21:31.653Apparently [Infinity] is not needed. Thanks. – DavidC – 2014-04-21T18:57:33.223