roget_components.w
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:17k
源码类别:

通讯编程

开发平台:

Visual C++

  1. % This file is part of the Stanford GraphBase (c) Stanford University 1993
  2. @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES!
  3. @i gb_types.w
  4. deftitle{ROGET_,COMPONENTS}
  5. def<#1>{$langle${rm#1}$rangle$}
  6. prerequisite{GB_,ROGET}
  7. @* Strong components. This demonstration program computes the
  8. strong components of GraphBase graphs derived from Roget's Thesaurus,
  9. using a variant of Tarjan's algorithm [R. E. Tarjan, ``Depth-first
  10. @^Tarjan, Robert Endre@>
  11. search and linear graph algorithms,'' {sl SIAM Journal on Computing/
  12. bf1} (1972), 146--160]. We also determine the relationships
  13. between strong components.
  14. Two vertices belong to the same strong component if and only if they
  15. are reachable from each other via directed paths.
  16. We will print the strong components in ``reverse topological order'';
  17. that is, if |v| is reachable from~|u| but |u| is not reachable
  18. from~|v|, the strong component containing~|v| will be listed before
  19. the strong component containing~|u|.
  20. Vertices from the |roget| graph are identified both by name and by
  21. category number.
  22. @d specs(v) (filename? v-g->vertices+1L: v->cat_no), v->name
  23.   /* category number and category name */
  24. @ We permit command-line options in UNIX/ style so that a variety of
  25. graphs can be studied:
  26. The user can say `.{-n}<number>', `.{-d}<number>', `.{-p}<number>',
  27. and/or `.{-s}<number>' to change the default values of the parameters
  28. in the graph |roget(n,d,p,s)|. Or `.{-g}<filename>' to change the
  29. graph itself.
  30. @^UNIX dependencies@>
  31. @p
  32. #include "gb_graph.h" /* the GraphBase data structures */
  33. #include "gb_roget.h" /* the |roget| routine */
  34. #include "gb_save.h" /* |restore_graph| */
  35. @h@#
  36. @<Global variables@>@;
  37. main(argc,argv)
  38.   int argc; /* the number of command-line arguments */
  39.   char *argv[]; /* an array of strings containing those arguments */
  40. {@+Graph *g; /* the graph we will work on */
  41.   register Vertex *v; /* the current vertex of interest */
  42.   unsigned long n=0; /* the desired number of vertices (0 means infinity) */
  43.   unsigned long d=0; /* the minimum distance between categories in arcs */
  44.   unsigned long p=0; /* 65536 times the probability of rejecting an arc */
  45.   long s=0; /* the random number seed */
  46.   char *filename=NULL; /* external graph substituted for |roget| */
  47.   @<Scan the command-line options@>;
  48.   g=(filename? restore_graph(filename): roget(n,d,p,s));
  49.   if (g==NULL) {
  50.     fprintf(stderr,"Sorry, can't create the graph! (error code %ld)n",
  51.              panic_code);
  52.     return -1;
  53.   }
  54.   printf("Reachability analysis of %snn",g->id);
  55.   @<Perform Tarjan's algorithm on |g|@>;
  56.   return 0; /* normal exit */
  57. }
  58. @ @<Scan the command-line options@>=
  59. while (--argc) {
  60. @^UNIX dependencies@>
  61.   if (sscanf(argv[argc],"-n%lu",&n)==1) ;
  62.   else if (sscanf(argv[argc],"-d%lu",&d)==1) ;
  63.   else if (sscanf(argv[argc],"-p%lu",&p)==1) ;
  64.   else if (sscanf(argv[argc],"-s%ld",&s)==1) ;
  65.   else if (strncmp(argv[argc],"-g",2)==0) filename=argv[argc]+2;
  66.   else {
  67.     fprintf(stderr,"Usage: %s [-nN][-dN][-pN][-sN][-gfoo]n",argv[0]);
  68.     return -2;
  69.   }
  70. }
  71. @ Tarjan's algorithm is inherently recursive. We will implement
  72. the recursion explicitly via linked lists, instead of using CEE/'s runtime
  73. stack, because some computer systems
  74. bog down in the presence of deeply nested recursion.
  75. Each vertex goes through three stages during the algorithm: First it is
  76. ``unseen''; then it is ``active''; finally it becomes ``settled,'' when it
  77. has been assigned to a strong component.
  78. The data structures that represent the current state of the algorithm
  79. are implemented by using five of the utility fields in each vertex:
  80. |rank|, |parent|, |untagged|, |link|, and |min|. We will consider each of
  81. these in turn.
  82. @ First is the integer |rank| field, which is zero when a vertex is unseen.
  83. As soon as the vertex is first examined, it becomes active and its |rank|
  84. becomes and remains nonzero. Indeed, the $k$th vertex to become active
  85. will receive rank~$k$. When a vertex finally becomes settled, its rank
  86. is reset to infinity.
  87. It's convenient to think of Tarjan's algorithm as a simple adventure
  88. game in which we want to explore all the rooms of a cave. Passageways between
  89. the rooms allow one-way travel only. When we come
  90. into a room for the first time, we assign a new number to that room;
  91. this is its rank. Later on we might happen to enter the same room
  92. again, and we will notice that it has nonzero rank. Then we'll be able
  93. to make a quick exit, saying ``we've already been here.'' (The extra
  94. complexities of computer games, like dragons that might need to be
  95. vanquished, do not arise.)
  96. @d rank z.I /* the |rank| of a vertex is stored in utility field |z| */
  97. @<Glob...@>=
  98. long nn; /* the number of vertices that have been seen */
  99. @ The active vertices will always form an oriented tree, whose arcs are
  100. a subset of the arcs in the original graph. A tree arc from |u| to~|v|
  101. will be represented by |v->parent==u|. Every active vertex has a
  102. parent, which is usually another active vertex; the only exception is
  103. the root of the tree, whose |parent| is |NULL|.
  104. In the cave analogy, the ``parent'' of room |v| is the room we were in
  105. immediately before entering |v| the first time. By following parent
  106. pointers, we will be able to leave the cave whenever we want.
  107. As soon as a vertex becomes settled, its |parent| field changes
  108. significance.  Then |v->parent| is set equal to the unique
  109. representative of the strong component containing vertex~|v|. Thus
  110. two settled vertices will belong to the same strong component if and only
  111. if they have the same |parent|.
  112. @d parent y.V /* the |parent| of a vertex is stored in utility field |y| */
  113. @ All arcs in the original directed graph are explored systematically during
  114. a depth-first search. Whenever we look at an arc, we tag it so that
  115. we won't need to explore it again. In a cave, for example, we might
  116. mark each passageway between rooms once we've tried to go through it.
  117. The algorithm doesn't actually place a tag on its |Arc| records; instead,
  118. each vertex |v| has a pointer |v->untagged| that leads to all
  119. hitherto-unexplored arcs from~|v|. The arcs of the list that appear
  120. between |v->arcs| and |v->untagged| are the ones already examined.
  121. @d untagged x.A
  122.  /* the |untagged| field points to an |Arc| record, or |NULL| */ 
  123. @ The algorithm maintains two special stacks: |active_stack| contains
  124. all the currently active vertices, and |settled_stack| contains all the
  125. currently settled vertices. Each vertex has a |link| field that points
  126. to the vertex that is next lower on its stack, or to |NULL| if the vertex is
  127. at the bottom. The vertices on |active_stack| always appear in increasing
  128. order of rank from bottom to top.
  129. @d link w.V /* the |link| field of a vertex occupies utility field |w| */
  130. @<Glob...@>=
  131. Vertex * active_stack; /* the top of the stack of active vertices */
  132. Vertex * settled_stack; /* the top of the stack of settled vertices */
  133. @ Finally there's a |min| field, which is the tricky part that makes
  134. everything work. If vertex~|v| is unseen or settled, its |min| field is
  135. irrelevant. Otherwise |v->min| points to the active vertex~|u|
  136. of smallest rank having the following property:
  137. Either |u==v| or there is a directed path from |v| to |u| consisting of
  138. zero or more mature tree arcs followed by a single non-tree arc.
  139. What is a tree arc, you ask. And what is a mature arc? Good questions. At the
  140. moment when arcs of the graph are tagged, we classify them either as tree
  141. arcs (if they correspond to a new |parent| link in the tree of active
  142. nodes) or non-tree arcs (otherwise). A tree arc becomes mature when it
  143. is no longer on the path from the root to the current vertex being
  144. explored. We also say that a vertex becomes mature when it is
  145. no longer on that path. All arcs from a mature vertex have been tagged.
  146. We said before that every vertex is initially unseen, then active, and
  147. finally settled. With our new definitions, we see further that every arc starts
  148. out untagged, then it becomes either a non-tree arc or a tree arc. In the
  149. latter case, the arc begins as an immature tree arc and eventually matures.
  150. Just believe these definitions, for now. All will become clear soon.
  151. @d min v.V /* the |min| field of a vertex occupies utility field |v| */
  152. @ Depth-first search explores a graph by systematically visiting all
  153. vertices and seeing what they can lead to. In Tarjan's algorithm, as
  154. we have said, the active vertices form an oriented tree. One of these
  155. vertices is called the current vertex.
  156. If the current vertex still has an arc that hasn't been tagged, we
  157. tag one such arc and there are two cases: Either the arc leads to
  158. an unseen vertex, or it doesn't. If it does, the arc becomes a tree
  159. arc; the previously unseen vertex becomes active, and it becomes the
  160. new current vertex.  On the other hand if the arc leads to a vertex
  161. that has already been seen, the arc becomes a non-tree arc and the
  162. current vertex doesn't change.
  163. Finally there will come a time when the current vertex~|v| has no
  164. untagged arcs. At this point, the
  165. algorithm might decide that |v| and all its descendants form a strong
  166. component. Indeed, this condition turns out to be true if and only if
  167. |v->min==v|; a proof appears below. If so, |v| and all its descendants
  168. become settled, and they leave the tree. If not, the tree arc from
  169. |v|'s parent~|u| to~|v| becomes mature, so the value of |v->min| is
  170. used to update the value of |u->min|. In both cases, |v| becomes mature
  171. and the new current vertex will be the parent of~|v|. Notice that only the
  172. value of |u->min| needs to be updated, when the arc from |u| to~|v|
  173. matures; all other values |w->min| stay the same, because a newly
  174. mature arc has no mature predecessors.
  175. The cave analogy helps to clarify the situation: If there's no way out
  176. of the subcave starting at~|v| unless we come back through |v| itself,
  177. and if we can get back to |v| from all its descendants, then
  178. room~|v| and its descendants will become a strong component. Once
  179. such a strong component is identified, we close it off and don't
  180. explore that subcave any further.
  181. If |v| is the root of the tree, it always has |v->min==v|,
  182. so it will always define a new strong component at the moment it matures.
  183. Then the depth-first search will terminate, since |v|~has no parent.
  184. But Tarjan's algorithm will press on, trying to find a vertex~|u| that is still
  185. unseen. If such a vertex exists,
  186. a new depth-first search will begin with |u| as the root. This
  187. process keeps on going until at last all vertices are happily settled.
  188. The beauty of this algorithm is that it all works very efficiently
  189. when we organize it as follows:
  190. @<Perform Tarjan's algorithm on |g|@>=
  191. @<Make all vertices unseen and all arcs untagged@>;
  192. for (vv=g->vertices; vv<g->vertices+g->n; vv++)
  193.   if (vv->rank==0) /* |vv| is still unseen */
  194.     @<Perform a depth-first search with |vv| as the root, finding the
  195.       strong components of all unseen vertices reachable from~|vv|@>;
  196. @<Print out one representative of each arc that runs
  197.     between strong components@>;
  198. @ @<Glob...@>=
  199. Vertex *vv; /* sweeps over all vertices, making sure none is left unseen */
  200. @ It's easy to get the data structures started, according to the
  201. conventions stipulated above.
  202. @<Make all vertices unseen...@>=
  203. for (v=g->vertices+g->n-1; v>=g->vertices; v--) {
  204.   v->rank=0;
  205.   v->untagged=v->arcs;
  206. }
  207. nn=0;
  208. active_stack=settled_stack=NULL;
  209. @ The task of starting a depth-first search isn't too bad either. Throughout
  210. this part of the algorithm, variable~|v| will point to the current vertex.
  211. @<Perform a depth-first search with |vv| as the root...@>=
  212. {
  213.   v=vv;
  214.   v->parent=NULL;
  215.   @<Make vertex |v| active@>;
  216.   do @<Explore one step from the current vertex~|v|, possibly moving
  217.         to another current vertex and calling~it~|v|@>@;
  218.   while (v!=NULL);
  219. }
  220. @ @<Make vertex |v| active@>=
  221. v->rank=++nn;
  222. v->link=active_stack;
  223. active_stack=v;
  224. v->min=v;
  225. @ Now things get interesting. But we're just doing what any well-organized
  226. spelunker would do when calmly exploring a cave.
  227. There are three main cases,
  228. depending on whether the current vertex stays where it is, moves
  229. to a new child, or backtracks to a parent.
  230. @<Explore one step from the current vertex~|v|, possibly moving
  231.         to another current vertex and calling~it~|v|@>=
  232. {@+register Vertex *u; /* a vertex adjacent to |v| */
  233.   register Arc *a=v->untagged; /* |v|'s first remaining untagged arc, if any */
  234.   if (a) {
  235.     u=a->tip;
  236.     v->untagged = a->next; /* tag the arc from |v| to |u| */
  237.     if (u->rank) { /* we've seen |u| already */
  238.       if (u->rank < v->min->rank)
  239.         v->min=u; /* non-tree arc, just update |v->min| */
  240.     }@+else { /* |u| is presently unseen */
  241.       u->parent = v; /* the arc from |v| to |u| is a new tree arc */
  242.       v = u; /* |u| will now be the current vertex */
  243.       @<Make vertex |v| active@>;
  244.     }
  245.   }@+else { /* all arcs from |v| are tagged, so |v| matures */
  246.     u=v->parent; /* prepare to backtrack in the tree */
  247.     if (v->min==v) @<Remove |v| and all its successors on the active stack
  248.          from the tree, and mark them as a strong component of the graph@>@;
  249.     else  /* the arc from |u| to |v| has just matured,
  250.              making |v->min| visible from |u| */@,
  251.       if (v->min->rank < u->min->rank)
  252.         u->min=v->min;
  253.     v=u; /* the former parent of |v| is the new current vertex |v| */
  254.   }
  255. }
  256. @ The elements of the active stack are always in order
  257. by rank, and all children of a vertex~|v| in the tree have rank higher
  258. than~|v|. Tarjan's algorithm relies on a converse property: {sl All
  259. active nodes whose rank exceeds that of the current vertex~|v| are
  260. descendants of~|v|.} (This property holds because the algorithm has constructed
  261. the tree by assigning ranks in preorder, ``the order of succession to the
  262. throne.'' First come |v|'s firstborn and descendants, then the nextborn,
  263. and so on.) Therefore the descendants of the current vertex always appear
  264. consecutively at the top of the stack.
  265. Another fundamental property of Tarjan's algorithm is more subtle:
  266. {sl There is always a way to get from any active vertex to the
  267. current vertex.} This follows from the fact that all mature active vertices~|u|
  268. have |u->min->rank<u->rank|.  If some active vertex does not lead to the
  269. current vertex~|v|,
  270. let |u| be the counterexample with smallest rank. Then |u| isn't an
  271. ancestor of~|v|, hence |u| must be mature; hence it leads to the
  272. active vertex |u->min|, from which there {sl is/} a path to~|v|,
  273. contradicting our assumption.
  274. Therefore |v| and its active descendants are all reachable from each
  275. other, and they must belong to the same strong component. Moreover, if
  276. |v->min=v|, this component can't be made any larger. For there is no
  277. arc from any of these vertices to an unseen vertex; all arcs from |v|
  278. and its descendants have already been tagged. And there is no arc from
  279. any of these vertices to an active vertex that is below |v| on the
  280. stack; otherwise |v->min| would have smaller rank than~|v|. Hence all
  281. arcs, if any, that lead from these vertices to some other vertex must
  282. lead to settled vertices. And we know from previous steps of the
  283. computation that the settled vertices all belong to other strong
  284. components.
  285. Therefore we are justified in settling |v| and its active descendants now.
  286. Removing them from the tree of active vertices does not remove any
  287. vertex from which there is a path to a vertex of rank less than |v->rank|.
  288. Hence their removal does not affect the validity of the |u->min| value
  289. for any vertex~|u| that remains active.
  290. We print out enough information for a reader to verify the
  291. strength of the claimed component easily.
  292. @d infinity g->n /* infinite rank (or close enough) */
  293. @<Remove |v| and all its successors on the active stack
  294.          from the tree, and mark them as a strong component of the graph@>=
  295. {@+register Vertex *t; /* runs through the vertices of the
  296.                         new strong component */
  297.   t=active_stack;
  298.   active_stack=v->link;
  299.   v->link=settled_stack;
  300.   settled_stack=t;  /* we've moved the top of one stack to the other */
  301.   printf("Strong component `%ld %s'", specs(v));
  302.   if (t==v) putchar('n'); /* single vertex */
  303.   else {
  304.     printf(" also includes:n");
  305.     while (t!=v) {
  306.       printf(" %ld %s (from %ld %s; ..to %ld %s)n",
  307.               specs(t), specs(t->parent), specs(t->min));
  308.       t->rank=infinity; /* now |t| is settled */
  309.       t->parent=v; /* and |v| represents the new strong component */
  310.       t=t->link;
  311.     }
  312.   }
  313.   v->rank=infinity; /* |v| too is settled */
  314.   v->parent=v; /* and represents its own strong component */
  315. }
  316. @ After all the strong components have been found, we can also compute the
  317. relations between them, without mentioning any cross-connection more than
  318. once. In fact, we built the |settled_stack| precisely so that this task
  319. could be done easily without sorting or searching. If only the strong
  320. components themselves were of interest, this part of the algorithm wouldn't
  321. be necessary.
  322. For this step we use the name |arc_from| for the field we previously
  323. called |untagged|. The trick here relies on the fact that all vertices of the
  324. same strong component appear together in |settled_stack|.
  325. @d arc_from x.V /* utility field |x| will now point to a vertex */
  326. @<Print out one representative of each arc that runs between...@>=
  327. printf("nLinks between components:n");
  328. for (v=settled_stack; v; v=v->link) {@+register Vertex *u=v->parent;
  329.   register Arc *a;
  330.   u->arc_from=u;
  331.   for (a=v->arcs; a; a=a->next) {@+register Vertex *w=a->tip->parent;
  332.     if (w->arc_from!=u) {
  333.       w->arc_from=u;
  334.       printf("%ld %s -> %ld %s (e.g., %ld %s -> %ld %s)n",
  335.               specs(u),specs(w),specs(v),specs(a->tip));
  336.     }
  337.   }
  338. }
  339. @* Index. We close with a list that shows where the identifiers of this
  340. program are defined and used.