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

通讯编程

开发平台:

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{FOOTBALL}
  5. prerequisite{GB_,GAMES}
  6. @* Introduction. This demonstration program uses graphs
  7. constructed by the {sc GB_,GAMES} module to produce
  8. an interactive program called .{football}, which finds preposterously
  9. long chains of scores to ``prove'' that one given team might outrank another
  10. by a huge margin.
  11. def<#1>{$langle${rm#1}$rangle$}
  12. The program prompts you for a starting team. If you simply type <return>,
  13. it exits; otherwise you should enter a team name (e.g., `.{Stanford}')
  14. before typing <return>.
  15. Then the program prompts you for another team. If you simply type
  16. <return> at this point, it will go back and ask for a new starting team;
  17. otherwise you should specify another name (e.g., `.{Harvard}').
  18. Then the program finds and displays a chain from the starting team
  19. to the other one. For example, you might see the following:
  20. $$vbox{halign{tt#hfilcr
  21.  Oct 06: Stanford Cardinal 36, Notre Dame Fighting Irish 31 (+5)cr
  22.  Oct 20: Notre Dame Fighting Irish 29, Miami Hurricanes 20 (+14)cr
  23.  Jan 01: Miami Hurricanes 46, Texas Longhorns 3 (+57)cr
  24.  Nov 03: Texas Longhorns 41, Texas Tech Red Raiders 22 (+76)cr
  25.  Nov 17: Texas Tech Red Raiders 62, Southern Methodist Mustangs 7 (+131)cr
  26.  Sep 08: Southern Methodist Mustangs 44, Vanderbilt Commodores 7 (+168)cr
  27. omitqquadvdotscr
  28.  Nov 10: Cornell Big Red 41, Columbia Lions 0 (+2188)cr
  29.  Sep 15: Columbia Lions 6, Harvard Crimson 9 (+2185)cr}}$$
  30. The chain isn't necessarily optimal; it's just this particular
  31. program's best guess. Another chain, which establishes a victory margin
  32. of $+2279$ points, can in fact be produced by modifying this
  33. program to search back from Harvard instead of forward from Stanford.
  34. Algorithms that find even better chains should be fun to invent.
  35. Actually this program has two variants. If you invoke it by saying simply
  36. `.{football}', you get chains found by a simple ``greedy algorithm.''
  37. But if you invoke it by saying `.{football} <number>', assuming UNIX/
  38. command-line conventions, the program works harder. Higher values of
  39. <number> do more calculation and tend to find better chains. For
  40. example, the simple greedy algorithm favors Stanford over Harvard by
  41. only 781; .{football}~.{10} raises this to 1895; the
  42. example above corresponds to .{football}~.{4000}.
  43. @ Here is the general program layout, as seen by the CEE/ compiler:
  44. @^UNIX dependencies@>
  45. @p
  46. #include "gb_graph.h" /* the standard GraphBase data structures */
  47. #include "gb_games.h" /* the routine that sets up the graph of scores */
  48. #include "gb_flip.h" /* random number generator */
  49. @h@#
  50. @<Type declarations@>@;
  51. @<Global variables@>@;
  52. @<Subroutines@>@;
  53. main(argc,argv)
  54.   int argc; /* the number of command-line arguments */
  55.   char *argv[]; /* an array of strings containing those arguments */
  56. {
  57.   @<Scan the command-line options@>;
  58.   @<Set up the graph@>;
  59.   while(1) {
  60.     @<Prompt for starting team and goal team; |break| if none given@>;
  61.     @<Find a chain from |start| to |goal|, and print it@>;
  62.   }
  63.   return 0; /* normal exit */
  64. }
  65. @ Let's deal with UNIX/-dependent stuff first. The rest of this program
  66. should work without change on any operating system.
  67. @^UNIX dependencies@>
  68. @<Scan the command-line options@>=
  69. if (argc==3 && strcmp(argv[2],"-v")==0) verbose=argc=2; /* secret option */
  70. if (argc==1) width=0;
  71. else if (argc==2 && sscanf(argv[1],"%ld",&width)==1) {
  72.   if (width<0) width=-width; /* a UNIX/ user might have used a hyphen */
  73. }@+else {
  74.   fprintf(stderr,"Usage: %s [searchwidth]n",argv[0]);
  75.   return -2;
  76. }
  77. @ @<Glob...@>=
  78. long width; /* number of cases examined per stratum */
  79. Graph *g; /* the graph containing score information */
  80. Vertex *u,*v; /* vertices of current interest */
  81. Arc *a; /* arc of current interest */
  82. Vertex *start,*goal; /* teams specified by the user */
  83. long mm; /* counter used only in |verbose| mode */
  84. @ An arc from |u| to |v| in the graph generated by |games| has a |len| field
  85. equal to the number of points scored by |u| against |v|.
  86. For our purposes we want also a |del| field, which gives the difference
  87. between the number of points scored by |u| and the number of points
  88. scored by~|v| in that game.
  89. @d del a.I /* |del| info appears in utility field |a| of an |Arc| record */
  90. @<Set up the graph@>=
  91. g=games(0L,0L,0L,0L,0L,0L,0L,0L);
  92.  /* this default graph has the data for the entire 1990 season */
  93. if (g==NULL) {
  94.   fprintf(stderr,"Sorry, can't create the graph! (error code %ld)n",
  95.             panic_code);
  96.   return -1;
  97. }
  98. for (v=g->vertices;v<g->vertices+g->n;v++)
  99.   for (a=v->arcs;a;a=a->next)
  100.     if (a->tip>v) { /* arc |a+1| is the mate of arc |a| iff |a->tip>v| */
  101.       a->del=a->len-(a+1)->len;
  102.       (a+1)->del=-a->del;
  103.     }
  104. @* Terminal interaction. While we're getting trivialities out of the way,
  105. we might as well take care of the simple dialog that transpires
  106. between this program and the user.
  107. @<Prompt for...@>=
  108. putchar('n'); /* make a blank line for visual punctuation */
  109. restart: /* if we avoid this label, the |break| command will be broken */
  110. if ((start=prompt_for_team("Starting"))==NULL) break;
  111. if ((goal=prompt_for_team("   Other"))==NULL) goto restart;
  112. if (start==goal) {
  113.   printf(" (Um, please give me the names of two DISTINCT teams.)n");
  114.   goto restart;
  115. }
  116. @ The user must spell team names exactly as they appear in the file
  117. .{games.dat}. Thus, for example, `.{Berkeley}' and `.{Cal}' don't
  118. work; it has to be `.{California}'. Similarly, a person must type
  119. `.{Pennsylvania}' instead of `.{Penn}', `.{Nevada-Las} .{Vegas}'
  120. instead of `.{UNLV}'. A backslash is necessary in `.{Texas} .{A\&M}'.
  121. @<Sub...@>=
  122. Vertex *prompt_for_team(s)
  123.   char *s; /* string used in prompt message */
  124. {@+register char *q; /* current position in |buffer| */
  125.   register Vertex *v; /* current vertex being examined in sequential search */
  126.   char buffer[30]; /* a line of input */
  127.   while (1) {
  128.     printf("%s team: ",s);
  129.     fflush(stdout); /* make sure the user sees the prompt */
  130.     fgets(buffer,30,stdin);
  131.     if (buffer[0]=='n') return NULL; /* the user just hit <return> */
  132.     buffer[29]='n';
  133.     for (q=buffer;*q!='n';q++) ; /* scan to end of input */
  134.     *q='';
  135.     for (v=g->vertices;v<g->vertices+g->n;v++)
  136.       if (strcmp(buffer,v->name)==0) return v; /* aha, we found it */
  137.     printf(" (Sorry, I don't know any team by that name.)n");
  138.     printf(" (One team I do know is %s...)n",
  139.              (g->vertices+gb_unif_rand(g->n))->name);
  140.   }
  141. }
  142. @*Greed. This program's primary task is to find the longest possible
  143. simple path from |start| to |goal|, using |del| as the length of each
  144. arc in the path. This is an NP-complete problem, and the number of
  145. possibilities is pretty huge, so the present program is content to
  146. use heuristics that are reasonably easy to compute. (Researchers are hereby
  147. challenged to come up with better heuristics. Does simulated annealing
  148. give good results? How about genetic algorithms?)
  149. Perhaps the first approach that comes to mind is a simple ``greedy'' approach
  150. in which each step takes the largest possible |del| that doesn't prevent
  151. us from eventually getting to |goal|. So that's the method we will
  152. implement first.
  153. @ @<Find a chain from |start| to |goal|, and print it@>=
  154. @<Initialize the allocation of auxiliary memory@>;
  155. if (width==0) @<Use a simple-minded
  156.   greedy algorithm to find a chain from |start| to |goal|@>@;
  157. else @<Use a stratified heuristic to find a chain from |start| to |goal|@>;
  158. @<Print the solution corresponding to |cur_node|@>;
  159. @<Recycle the auxiliary memory used@>;
  160. @ We might as well use data structures that are more general than we need,
  161. in anticipation of a more complex heuristic that will be implemented later.
  162. The set of all possible solutions can be viewed as a backtrack tree
  163. in which the branches from each node are the games that can possibly
  164. follow that node. We will examine a small part of that gigantic tree.
  165. @<Type declarations@>=
  166. typedef struct node_struct {
  167.   Arc *game; /* game from the current team to the next team */
  168.   long tot_len; /* accumulated length from |start| to here */
  169.   struct node_struct *prev; /* node that gave us the current team */
  170.   struct node_struct *next;
  171.     /* list pointer to node in same stratum (see below) */
  172. } node;
  173. @ @<Glob...@>=
  174. Area node_storage; /* working storage for heuristic calculations */
  175. node *next_node; /* where the next node is slated to go */
  176. node *bad_node; /* end of current allocation block */
  177. node *cur_node; /* current node of particular interest */
  178. @ @<Initialize the allocation of auxiliary memory@>=
  179. next_node=bad_node=NULL;
  180. @ @<Subroutines@>=
  181. node *new_node(x,d)
  182.   node *x; /* an old node that the new node will call |prev| */
  183.   long d; /* incremental change to |tot_len| */
  184. {
  185.   if (next_node==bad_node) {
  186.     next_node=gb_typed_alloc(1000,node,node_storage);
  187.     if (next_node==NULL) return NULL; /* we're out of space */
  188.     bad_node=next_node+1000;
  189.   }
  190.   next_node->prev=x;
  191.   next_node->tot_len=(x?x->tot_len:0)+d;
  192.   return next_node++;
  193. }
  194. @ @<Recycle the auxiliary memory used@>=
  195. gb_free(node_storage);
  196. @ When we're done, |cur_node->game->tip| will be the |goal| vertex, and
  197. we can get back to the |start| vertex by following |prev| links
  198. from |cur_node|. It looks better to print the answers from |start| to
  199. |goal|, so maybe we should have changed our algorithm to go the
  200. other way.
  201. But let's not worry over trifles. It's easy to change
  202. the order of a linked list. The secret is simply to think of the list
  203. as a stack, from which we pop all the elements off to another stack;
  204. the new stack has the elements in reverse order.
  205. @<Print the solution corresponding to |cur_node|@>=
  206. next_node=NULL; /* now we'll use |next_node| as top of temporary stack */
  207. do@+{@+register node*t;
  208.   t=cur_node;
  209.   cur_node=t->prev; /* pop */
  210.   t->prev=next_node;
  211.   next_node=t; /* push */
  212. }@+while (cur_node);
  213. for (v=start;v!=goal;v=u,next_node=next_node->prev) {
  214.   a=next_node->game;
  215.   u=a->tip;
  216.   @<Print the score of game |a| between |v| and |u|@>;
  217.   printf(" (%+ld)n",next_node->tot_len);
  218. }
  219. @ @<Print the score of game |a| between |v| and |u|@>=
  220. {@+register long d=a->date; /* date of the game, 0 means Aug 26 */
  221.   if (d<=5) printf(" Aug %02ld",d+26);
  222.   else if (d<=35) printf(" Sep %02ld",d-5);
  223.   else if (d<=66) printf(" Oct %02ld",d-35);
  224.   else if (d<=96) printf(" Nov %02ld",d-66);
  225.   else if (d<=127) printf(" Dec %02ld",d-96);
  226.   else printf(" Jan 01"); /* |d=128| */
  227.   printf(": %s %s %ld, %s %s %ld",v->name,v->nickname,a->len,
  228.                                 u->name,u->nickname,a->len-a->del);
  229. }
  230. @ We can't just move from |v| to any adjacent vertex; we can go only
  231. to a vertex from which |goal| can be reached without touching |v|
  232. or any other vertex already used on the path from |start|.
  233. Furthermore, if the locally best move from |v| is directly to |goal|,
  234. we don't want to make that move unless it's our last chance; we can
  235. probably do better by making the chain longer. Otherwise, for example,
  236. a chain between a team and its worst opponent would consist of
  237. only a single game.
  238. To keep track of untouchable vertices, we use a utility field
  239. called |blocked| in each vertex record. Another utility field,
  240. |valid|, will be set to a validation code in each vertex that
  241. still leads to the goal.
  242. @d blocked u.I
  243. @d valid v.V
  244. @<Use a simple-minded greedy algorithm to find a chain...@>=
  245. {
  246.   for (v=g->vertices;v<g->vertices+g->n;v++) v->blocked=0,v->valid=NULL;
  247.   cur_node=NULL;
  248.   for (v=start;v!=goal;v=cur_node->game->tip) {@+register long d=-10000;
  249.     register Arc *best_arc; /* arc that achieves |del=d| */
  250.     register Arc *last_arc; /* arc that goes directly to |goal| */
  251.     v->blocked=1;
  252.     cur_node=new_node(cur_node,0L);
  253.     if (cur_node==NULL) {
  254.       fprintf(stderr,"Oops, there isn't enough memory!n");@+return -2;
  255.     }
  256.     @<Set |u->valid=v| for all |u| to which |v| might now move@>;
  257.     for (a=v->arcs;a;a=a->next)
  258.       if (a->del>d && a->tip->valid==v)
  259.         if (a->tip==goal) last_arc=a;
  260.         else best_arc=a,d=a->del;
  261.     cur_node->game=(d==-10000?last_arc:best_arc);
  262.                  /* use |last_arc| as a last resort */
  263.     cur_node->tot_len+=cur_node->game->del;
  264.   }
  265. }
  266. @ A standard marking algorithm supplies the final missing link in
  267. our algorithm.
  268. @d link w.V
  269. @<Set |u->valid=v| for all |u| to which |v| might now move@>=
  270. u=goal; /* |u| will be the top of a stack of nodes to be explored */
  271. u->link=NULL;
  272. u->valid=v;
  273. do@+{
  274.   for (a=u->arcs,u=u->link;a;a=a->next)
  275.     if (a->tip->blocked==0 && a->tip->valid!=v) {
  276.       a->tip->valid=v; /* mark |a->tip| reachable from |goal| */
  277.       a->tip->link=u;
  278.       u=a->tip; /* push it on the stack, so that its successors
  279.                    will be marked too */
  280.     }
  281. }@+while (u);
  282. @*Stratified greed.  One approach to better chains is the following
  283. algorithm, motivated by similar ideas of Pang Chen [Ph.D. thesis,
  284. Stanford University, 1989]: @^Chen, Pang-Chieh@> Suppose the nodes of
  285. a (possibly huge) backtrack tree are classified into a (fairly small)
  286. number of strata, by a function $h$ with the property that $h({rm
  287. child})<h({rm parent})$ and $h({rm goal})=0$. Suppose further that
  288. we want to find a node $x$ that maximizes a given function~$f(x)$,
  289. where it is reasonable to believe that $f$(child) will be relatively
  290. large among nodes in a child's stratum only if $f$(parent) is
  291. relatively large in the parent's stratum. Then it makes sense to
  292. restrict backtracking to, say, the top $w$ nodes of each stratum,
  293. ranked by their $f$ values.
  294. The greedy algorithm already described is a special case of this general
  295. approach, with $w=1$ and with $h(x)=-($length of chain leading to~$x)$.
  296. The refined algorithm we are about to describe uses a general value of $w$
  297. and a somewhat more relevant stratification function: Given a node~$x$
  298. of the backtrack tree for longest paths, corresponding to a path from
  299. |start| to a certain vertex~$u=u(x)$, we will let $h(x)$ be the number of
  300. vertices that lie between |u| and |goal| (in the sense that the simple
  301. path from |start| to~|u| can be extended until it passes through such
  302. a vertex and then all the way to~|goal|).
  303. Here is the top level of the stratified greedy algorithm. We maintain
  304. a linked list of nodes for each stratum, that is, for each possible value
  305. of~$h$. The number of nodes required is bounded by $w$ times the
  306. number of strata.
  307. @<Use a strat...@>=
  308. {
  309.   @<Make |list[0]| through |list[n-1]| empty@>;
  310.   cur_node=NULL; /* |NULL| represents the root of the backtrack tree */
  311.   m=g->n-1; /* the highest stratum not yet fully explored */
  312.   do@+{
  313.     @<Place each child~|x| of |cur_node| into |list[h(x)]|, retaining
  314.       at most |width| nodes of maximum |tot_len| on each list@>;
  315.     while (list[m]==NULL)
  316.       @<Decrease |m| and get ready to explore another list@>;
  317.     cur_node=list[m];
  318.     list[m]=cur_node->next; /* remove a node from highest remaining stratum */
  319.     if (verbose) @<Print ``verbose'' info about |cur_node|@>;
  320.   }@+while (m>0); /* exactly one node should be in |list[0]| (see below) */
  321. }
  322. @ The calculation of $h(x)$ is somewhat delicate, and we will defer it
  323. for a moment. But the list manipulation is easy, so we can finish it
  324. quickly while it's fresh in our minds.
  325. @d MAX_N 120 /* the number of teams in .{games.dat} */
  326. @<Glob...@>=
  327. node *list[MAX_N]; /* the best nodes known in given strata */
  328. long size[MAX_N]; /* the number of elements in a given |list| */
  329. long m,h; /* current lists of interest */
  330. node *x; /* a child of |cur_node| */
  331. @ @<Make |list[0]|...@>=
  332. for (m=0;m<g->n;m++) list[m]=NULL,size[m]=0;
  333. @ The lists are maintained in order by |tot_len|, with the largest
  334. |tot_len| value at the end so that we can easily delete the smallest.
  335. When |h=0|, we retain only one node instead of~|width| different nodes,
  336. because we are interested in only one solution.
  337. @<Place node~|x| into |list[h]|, retaining
  338.     at most |width| nodes of maximum |tot_len|@>=
  339. if ((h>0 && size[h]==width) || (h==0 && size[0]>0)) {
  340.   if (x->tot_len<=list[h]->tot_len) goto done; /* drop node |x| */
  341.   list[h]=list[h]->next; /* drop one node from |list[h]| */
  342. }@+else size[h]++;
  343. {@+register node *p,*q; /* node in list and its predecessor */
  344.   for (p=list[h],q=NULL; p; q=p,p=p->next)@+
  345.     if (x->tot_len<=p->tot_len) break;
  346.   x->next=p;
  347.   if (q) q->next=x;@+ else list[h]=x;
  348. }
  349. done:;
  350. @ We reverse the list so that large entries will tend to go in first.
  351. @<Decrease |m| and get ready to explore another list@>=
  352. {@+register node *r=NULL, *s=list[--m], *t;
  353.   while (s) t=s->next, s->next=r, r=s, s=t;
  354.   list[m]=r;
  355.   mm=0; /* |mm| is an index for ``verbose'' printing */
  356. }
  357. @ @<Print ``verbose'' info...@>=
  358. {
  359.   cur_node->next=(node*)((++mm<<8)+m); /* pack an ID for this node */
  360.   printf("[%lu,%lu]=[%lu,%lu]&%s (%+ld)n",m,mm,@|
  361.     cur_node->prev?((unsigned long)cur_node->prev->next)&0xff:0L,@|
  362.     cur_node->prev?((unsigned long)cur_node->prev->next)>>8:0L,@|
  363.     cur_node->game->tip->name, cur_node->tot_len);
  364. }
  365. @ Incidentally, it is plausible to conjecture that the stratified algorithm
  366. always beats the simple greedy algorithm, but that conjecture is false.
  367. For example, the greedy algorithm is able to rank Harvard over Stanford
  368. by 1529, while the stratified algorithm achieves only 1527 when
  369. |width=1|. On the other hand, the greedy algorithm often fails
  370. miserably; when comparing two Ivy League teams, it doesn't find a
  371. way to break out of the Ivy and Patriot Leagues.
  372. @*Bicomponents revisited.
  373. How difficult is it to compute the function $h$? Given a connected graph~$G$
  374. with two distinguished vertices $u$ and~$v$, we want to count the number
  375. of vertices that might appear on a simple path from $u$ to~$v$.
  376. (This is {sl not/} the same as the number of vertices reachable from both
  377. $u$ and~$v$. For example, consider a ``claw'' graph with four vertices
  378. ${u,v,w,x}$ and with edges only from $x$ to the other three vertices;
  379. in this graph $w$ is reachable from $u$ and~$v$, but it is not on any simple
  380. path between them.)
  381. The best way to solve this problem is probably to compute the bicomponents
  382. of~$G$, or least to compute some of them. Another demo program,
  383. {sc BOOK_kern.05emCOMPONENTS}, explains the relevant theory in some
  384. detail, and we will assume familiarity with that algorithm in the present
  385. discussion.
  386. Let us imagine extending $G$ to a slightly larger graph $G^+$ by
  387. adding a dummy vertex~$o$ that is adjacent only to $v$. Suppose we determine
  388. the bicomponents of $G^+$ by depth-first search starting at~$o$.
  389. These bicomponents form a tree rooted at the bicomponent that contains
  390. just $o$ and~$v$. The number of vertices on paths between $u$ and~$v$,
  391. not counting $v$ itself, is then the number of vertices in the bicomponent
  392. containing~$u$ and in any other bicomponents between that one and the root.
  393. Strictly speaking, each articulation point belongs
  394. to two or more bicomponents. But we will assign each articulation point
  395. to its bicomponent that is nearest the root of the tree; then the vertices
  396. of each bicomponent are precisely the vertices output in bursts by the
  397. depth-first procedure. The bicomponents we want to enumerate are $B_1$, $B_2$,
  398. dots,~$B_k$, where $B_1$ is the bicomponent containing~$u$ and
  399. $B_{j+1}$ is the bicomponent containing the articulation point associated
  400. with~$B_j$; we stop at~$B_k$ when its associated articulation point is~$v$.
  401. (Often $k=1$.)
  402. The ``children'' of a given graph~$G$ are obtained by removing vertex~$u$
  403. and by considering paths from $u'$ to~$v$, where $u'$ is a vertex
  404. formerly adjacent to~$u$; thus $u'$ is either in~$B_1$ or it is $B_1$'s
  405. associated articulation point. Removing $u$ will, in general, split
  406. $B_1$ into a tree of smaller bicomponents, but $B_2,ldots,B_k$ will be
  407. unaffected. The implementation below does not take full advantage of this
  408. observation, because the amount of memory required to avoid recomputation
  409. would probably be prohibitive.
  410. @ The following program is copied almost verbatim from
  411. {sc BOOK_kern.05emCOMPONENTS}.
  412. Instead of repeating the commentary that appears there, we will mention
  413. only the significant differences. One difference is that we start
  414. the depth-first search at a definite place, the |goal|.
  415. @<Place each child~|x| of |cur_node| into |list[h(x)]|, retaining
  416.     at most |width| nodes of maximum |tot_len| on each list@>=
  417. @<Make all vertices unseen and all arcs untagged, except for vertices
  418.   that have already been used in steps leading up to |cur_node|@>;
  419. @<Perform a depth-first search with |goal| as the root, finding
  420.   bicomponents and determining the number of vertices accessible
  421.   between any given vertex and |goal|@>;
  422. for (a=(cur_node? cur_node->game->tip: start)->arcs; a; a=a->next)
  423.   if ((u=a->tip)->untagged==NULL) { /* |goal| is reachable from |u| */
  424.     x=new_node(cur_node,a->del);
  425.     if (x==NULL) {
  426.       fprintf(stderr,"Oops, there isn't enough memory!n");@+return -3;
  427.     }
  428.     x->game=a;
  429.     @<Set |h| to the number of vertices on paths between |u| and |goal|@>;
  430.     @<Place node...@>;
  431.   }
  432. @ Setting the |rank| field of a vertex to infinity before beginning
  433. a depth-first search is tantamount to removing that vertex from
  434. the graph, because it tells the algorithm not to look further at
  435. such a vertex.
  436. @d rank z.I /* when was this vertex first seen? */
  437. @d parent u.V /* who told me about this vertex? */
  438. @d untagged x.A /* what is its first untagged arc? */
  439. @d min v.V /* how low in the tree can we jump from its mature descendants? */
  440. @<Make all vertices unseen and all arcs untagged, except for vertices
  441.   that have already been used in steps leading up to |cur_node|@>=
  442. for (v=g->vertices; v<g->vertices+g->n; v++) {
  443.   v->rank=0;
  444.   v->untagged=v->arcs;
  445. }
  446. for (x=cur_node;x;x=x->prev)
  447.   x->game->tip->rank=g->n; /* ``infinite'' rank (or close enough) */
  448. start->rank=g->n;
  449. nn=0;
  450. active_stack=settled_stack=NULL;
  451. @ @<Glob...@>=
  452. Vertex * active_stack; /* the top of the stack of active vertices */
  453. Vertex *settled_stack; /* the top of the stack of bicomponents found */
  454. long nn; /* the number of vertices that have been seen */
  455. Vertex dummy; /* imaginary parent of |goal|; its |rank| is zero */
  456. @ The |settled_stack| will contain a list of all bicomponents in
  457. the opposite order from which they are discovered. This is the order
  458. we'll need later for computing the |h| function in each bicomponent.
  459. @<Perform a depth-first search...@>=
  460. {
  461.   v=goal;
  462.   v->parent=&dummy;
  463.   @<Make vertex |v| active@>;
  464.   do @<Explore one step from the current vertex~|v|, possibly moving
  465.         to another current vertex and calling~it~|v|@>@;
  466.   while (v!=&dummy);
  467.   @<Use |settled_stack| to put the mutual reachability count for
  468.      each vertex |u| in |u->parent->rank|@>;
  469. }
  470. @ @<Make vertex |v| active@>=
  471. v->rank=++nn;
  472. v->link=active_stack;
  473. active_stack=v;
  474. v->min=v->parent;
  475. @ @<Explore one step from the current vertex~|v|, possibly moving
  476.         to another current vertex and calling~it~|v|@>=
  477. {@+register Vertex *u; /* a vertex adjacent to |v| */
  478.   register Arc *a=v->untagged; /* |v|'s first remaining untagged arc, if any */
  479.   if (a) {
  480.     u=a->tip;
  481.     v->untagged = a->next; /* tag the arc from |v| to |u| */
  482.     if (u->rank) { /* we've seen |u| already */
  483.       if (u->rank < v->min->rank)
  484.         v->min=u; /* non-tree arc, just update |v->min| */
  485.     }@+else { /* |u| is presently unseen */
  486.       u->parent = v; /* the arc from |v| to |u| is a new tree arc */
  487.       v = u; /* |u| will now be the current vertex */
  488.       @<Make vertex |v| active@>;
  489.     }
  490.   }@+else { /* all arcs from |v| are tagged, so |v| matures */
  491.     u=v->parent; /* prepare to backtrack in the tree */
  492.     if (v->min==u) @<Remove |v| and all its successors on the active stack
  493.          from the tree, and report them as a bicomponent of the graph
  494.          together with~|u|@>@;
  495.     else  /* the arc from |u| to |v| has just matured,
  496.              making |v->min| visible from |u| */@,
  497.       if (v->min->rank < u->min->rank)
  498.         u->min=v->min;
  499.     v=u; /* the former parent of |v| is the new current vertex |v| */
  500.   }
  501. }
  502. @ When a bicomponent is found, we reset the |parent| field of each vertex
  503. so that, afterwards, two vertices will belong to the same bicomponent
  504. if and only if they have the same |parent|. (This trick was not used in
  505. {sc BOOK_kern.05emCOMPONENTS}, but it does appear in the similar algorithm
  506. of {sc ROGET_,COMPONENTS}.) The new parent, |v|, will represent that
  507. bicomponent in subsequent computation; we put it onto |settled_stack|.
  508. We also reset |v->rank| to be the bicomponent's size, plus a constant
  509. large enough to keep the algorithm from getting confused. (Vertex~|u|
  510. might still have untagged arcs leading into this bicomponent; we need to
  511. keep the ranks at least as big as the rank of |u->min|.) Notice that
  512. |v->min| is |u|, the articulation point associated with this bicomponent.
  513. Later the |rank| field will
  514. contain the sum of all counts between here and the root.
  515. We don't have to do anything when |v==goal|; the trivial root bicomponent
  516. always comes out last.
  517. @<Remove |v| and all its successors on the active stack...@>=
  518. {@+if (v!=goal) {@+register Vertex *t; /* runs through the vertices of the
  519.                           new bicomponent */
  520.     long c=0; /* the number of vertices removed */
  521.     t=active_stack;
  522.     while (t!=v) {
  523.       c++;
  524.       t->parent=v;
  525.       t=t->link;
  526.     }
  527.     active_stack=v->link;
  528.     v->parent=v;
  529.     v->rank=c+g->n; /* the true component size is |c+1| */
  530.     v->link=settled_stack;
  531.     settled_stack=v;
  532.   }
  533. }
  534. @ So here's how we sum the ranks. When we get to this step, the \{settled}
  535. stack contains all bicomponent representatives except |goal| itself.
  536. @<Use |settled_stack| to put the mutual reachability count for
  537.      each vertex |u| in |u->parent->rank|@>=
  538. while (settled_stack) {
  539.   v=settled_stack;
  540.   settled_stack=v->link;
  541.   v->rank+=v->min->parent->rank+1-g->n;
  542. } /* note that |goal->parent->rank=0| */
  543. @ And here's the last piece of the puzzle.
  544. @<Set |h| to the number of vertices on paths between |u| and |goal|@>=
  545. h=u->parent->rank;
  546. @* Index. Finally, here's a list that shows where the identifiers of this
  547. program are defined and used.