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

通讯编程

开发平台:

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{GB_,MILES}
  5. prerequisites{GB_,GRAPH}{GB_,IO}
  6. @* Introduction. This GraphBase module contains the |miles| subroutine,
  7. which creates a family of undirected graphs based on highway mileage data
  8. between North American cities. Examples of the use of this procedure can be
  9. found in the demo programs {sc MILES_,SPAN} and {sc GB_,PLANE}.
  10. @(gb_miles.h@>=
  11. extern Graph *miles();
  12. @ The subroutine call {advancethinmuskip 0mu plus 4mu
  13. |miles(n,north_weight,west_weight,pop_weight,max_distance,max_degree,seed)|}
  14. constructs a graph based on the information in .{miles.dat}.
  15. Each vertex of the graph corresponds to one of the 128 cities whose
  16. name is alphabetically greater than or equal to `Ravenna, Ohio' in
  17. the 1949 edition of Rand McNally {char`&} Company's {sl Standard Highway
  18. Mileage Guide}. Edges between vertices are assigned lengths representing
  19. distances between cities, in miles. In most cases these mileages come
  20. from the Rand McNally Guide, but several dozen entries needed to be changed
  21. drastically because they were obviously too large or too small; in such cases
  22. an educated guess was made. Furthermore, about 5% of the entries were
  23. adjusted slightly in order to
  24. ensure that all distances satisfy the ``triangle inequality'': The
  25. graph generated by |miles| has the property that the
  26. distance from |u| to~|v| plus the distance from |v| to~|w| always exceeds
  27. or equals the distance from |u| to~|w|.
  28. The constructed graph will have $min(n,128)$ vertices; the default value
  29. |n=128| is substituted if |n=0|. If |n| is less
  30. than 128, the |n| cities will be selected by assigning a weight to
  31. each city and choosing the |n| with largest weight, using random
  32. numbers to break ties in case of equal weights. Weights are computed
  33. by the formula
  34. $$ |north_weight|cdot|lat|+|west_weight|cdot|lon|+|pop_weight|cdot|pop|, $$
  35. where |lat| is latitude north of the equator, |lon| is longitude
  36. west of Greenwich, and |pop| is the population in 1980. Both |lat| and |lon|
  37. are given in ``centidegrees'' (hundredths of degrees). For example,
  38. San Francisco has |lat=3778|, |lon=12242|, and |pop=678974|;
  39. this means that, before the recent earthquake, it was located at
  40. $37.78^circ$ north latitude and $122.42^circ$ west longitude, and that it had
  41. 678,974 residents in the 1980 census. The weight parameters must satisfy
  42. $$ vert|north_weight|vertle100{,}000,quad
  43.    vert|west_weight|vertle100{,}000,quad
  44.    vert|pop_weight|vertle100.$$
  45. The constructed graph will be ``complete''---that is, it will have
  46. edges between every pair of vertices---unless special values are given to
  47. the parameters
  48. |max_distance| or |max_degree|. If |max_distance!=0|, edges with more
  49. than |max_distance| miles will not appear; if |max_degree!=0|, each
  50. vertex will be limited to at most |max_degree| of its shortest edges.
  51. Vertices of the graph will appear in order of decreasing weight.
  52. The |seed| parameter defines the pseudo-random numbers used wherever
  53. a ``random'' choice between equal-weight vertices or equal-length edges
  54. needs to be made.
  55. @d MAX_N 128
  56. @(gb_miles.h@>=
  57. #define MAX_N 128 /* maximum and default number of cities */
  58. @ Examples: The call |miles(100,0,0,1,0,0,0)| will construct a
  59. complete graph on 100 vertices, representing the 100 most populous
  60. cities in the database.  It turns out that San Diego, with a
  61. population of 875,538, is the winning city by this criterion, followed
  62. by San Antonio (population 786,023), San Francisco (678,974), and
  63. Washington D.C. (638,432).
  64. To get |n| cities in the western United States and Canada, you can say
  65. $|miles|(n,0,1,0,ldots,)$; to get |n| cities in the Northeast, use a
  66. call like $|miles|(n,1,-1,0,ldots,)$. A parameter setting like
  67. $(50,-500,0,1,ldots,)$ produces mostly Southern cities, except for a
  68. few large metropolises in the north.
  69. If you ask for |miles(n,a,b,c,0,1,0)|, you get an edge between cities if
  70. and only if each city is the nearest to the other, among the |n| cities
  71. selected. (The graph is always undirected: There is an arc from |u| to~|v|
  72. if and only if there's an arc of the same length from |v| to~|u|.)
  73. A random selection of cities can be obtained by calling
  74. |miles(n,0,0,0,m,d,s)|.  Different choices of the seed number |s| will
  75. produce different selections, in a system-independent manner;
  76. identical results will be obtained on all computers when identical
  77. parameters have been specified.  Equivalent experiments on algorithms
  78. for graph manipulation can therefore be performed by researchers in
  79. different parts of the world. Any value of |s| between 0 and
  80. $2^{31}-1$ is permissible.
  81. @ If the |miles| routine encounters a problem, it returns |NULL|
  82. (.{NULL}), after putting a code number into the external variable
  83. |panic_code|. This code number identifies the type of failure.
  84. Otherwise |miles| returns a pointer to the newly created graph, which
  85. will be represented with the data structures explained in {sc GB_,GRAPH}.
  86. (The external variable |panic_code| is itself defined in {sc GB_,GRAPH}.)
  87. @d panic(c) @+{@+panic_code=c;@+gb_trouble_code=0;@+return NULL;@+}
  88. @ The CEE/ file .{gb_miles.c} has the following overall shape:
  89. @p
  90. #include "gb_io.h" /* we will use the {sc GB_,IO} routines for input */
  91. #include "gb_flip.h"
  92.  /* we will use the {sc GB_,FLIP} routines for random numbers */
  93. #include "gb_graph.h" /* we will use the {sc GB_,GRAPH} data structures */
  94. #include "gb_sort.h" /* and the linksort routine */
  95. @h@#
  96. @<Type declarations@>@;
  97. @<Private variables@>@;
  98. @#
  99. Graph *miles(n,north_weight,west_weight,pop_weight,
  100.     max_distance,max_degree,seed)
  101.   unsigned long n; /* number of vertices desired */
  102.   long north_weight; /* coefficient of latitude in the weight function */
  103.   long west_weight; /* coefficient of longitude in the weight function */
  104.   long pop_weight; /* coefficient of population in the weight function */
  105.   unsigned long max_distance; /* maximum distance in an edge, if nonzero */
  106.   unsigned long max_degree;
  107.        /* maximum number of edges per vertex, if nonzero */
  108.   long seed; /* random number seed */
  109. {@+@<Local variables@>@;@#
  110.   gb_init_rand(seed);
  111.   @<Check that the parameters are valid@>;
  112.   @<Set up a graph with |n| vertices@>;
  113.   @<Read the data file .{miles.dat} and compute city weights@>;
  114.   @<Determine the |n| cities to use in the graph@>;
  115.   @<Put the appropriate edges into the graph@>;
  116.   if (gb_trouble_code) {
  117.     gb_recycle(new_graph);
  118.     panic(alloc_fault); /* oops, we ran out of memory somewhere back there */
  119.   }
  120.   return new_graph;
  121. }
  122. @ @<Local var...@>=
  123. Graph *new_graph; /* the graph constructed by |miles| */
  124. register long j,k; /* all-purpose indices */
  125. @ @<Check that the parameters are valid@>=
  126. if (n==0 || n>MAX_N) n=MAX_N;
  127. if (max_degree==0 || max_degree>=n) max_degree=n-1;
  128. if (north_weight>100000 || west_weight>100000 || pop_weight>100 @|
  129.  || north_weight<-100000 || west_weight<-100000 || pop_weight<-100)
  130.   panic(bad_specs); /* the magnitude of at least one weight is too big */
  131. @ @<Set up a graph with |n| vertices@>=
  132. new_graph=gb_new_graph(n);
  133. if (new_graph==NULL)
  134.   panic(no_room); /* out of memory before we're even started */
  135. sprintf(new_graph->id,"miles(%lu,%ld,%ld,%ld,%lu,%lu,%ld)",
  136.   n,north_weight,west_weight,pop_weight,max_distance,max_degree,seed);
  137. strcpy(new_graph->util_types,"ZZIIIIZZZZZZZZ");
  138. @* Vertices.  As we read in the data, we construct a list of nodes,
  139. each of which contains a city's name, latitude, longitude, population,
  140. and weight. These nodes conform to the specifications stipulated in
  141. the {sc GB_,SORT} module. After the list has been sorted by weight, the
  142. top |n| entries will be the vertices of the new graph.
  143. @<Type decl...@>=
  144. typedef struct node_struct { /* records to be sorted by |gb_linksort| */
  145.   long key; /* the nonnegative sort key (weight plus $2^{30}$) */
  146.   struct node_struct *link; /* pointer to next record */
  147.   long kk; /* index of city in the original database */
  148.   long lat,lon,pop; /* latitude, longitude, population */
  149.   char name[30]; /* |"City Name, ST"| */
  150. } node;
  151. @ The constants defined here are taken from the specific data in .{miles.dat},
  152. because this routine is not intended to be perfectly general.
  153. @<Private...@>=
  154. static long min_lat=2672, max_lat=5042, min_lon=7180, max_lon=12312,
  155.  min_pop=2521, max_pop=875538; /* tight bounds on data entries */
  156. static node *node_block; /* array of nodes holding city info */
  157. static long *distance; /* array of distances */
  158. @ The data in .{miles.dat} appears in 128 groups of lines, one for each
  159. city, in reverse alphabetical order. These groups have the general form
  160. $$vcenter{halign{tt#hfilcr
  161. City Name, ST[lat,lon]popcr
  162. d1 d2 d3 d4 d5 d6 ... (possibly several lines' worth)cr
  163. }}$$
  164. where .{City Name} is the name of the city (possibly including spaces);
  165. .{ST} is the two-letter state code; .{lat} and .{lon} are latitude
  166. and longitude in hundredths of degrees; .{pop} is the population; and
  167. the remaining numbers .{d1}, .{d2}, dots are distances to the
  168. previously named cities in reverse order. Each distance is separated
  169. from the previous item by either a blank space or a newline character.
  170. For example, the line
  171. $$hbox{tt San Francisco, CA[3778,12242]678974}$$
  172. specifies the data about San Francisco that was mentioned earlier.
  173. From the first few groups
  174. $$vcenter{halign{tt#hfilcr
  175. Youngstown, OH[4110,8065]115436cr
  176. Yankton, SD[4288,9739]12011cr
  177. 966cr
  178. Yakima, WA[4660,12051]49826cr
  179. 1513 2410cr
  180. Worcester, MA[4227,7180]161799cr
  181. 2964 1520 604cr
  182. }}$$
  183. we learn that the distance from Worcester, Massachusetts, to Yakima,
  184. Washington, is 2964 miles; from Worcester to Youngstown it is 604 miles.
  185. The following two-letter ``state codes'' are used for Canadian provinces:
  186. $.{BC}=null$British Columbia,
  187. $.{MB}=null$Manitoba,
  188. $.{ON}=null$Ontario,
  189. $.{SK}=null$Saskatchewan.
  190. @<Read the data file .{miles.dat} and compute city weights@>=
  191. node_block=gb_typed_alloc(MAX_N,node,new_graph->aux_data);
  192. distance=gb_typed_alloc(MAX_N*MAX_N,long,new_graph->aux_data);
  193. if (gb_trouble_code) {
  194.   gb_free(new_graph->aux_data);
  195.   panic(no_room+1); /* no room to copy the data */
  196. }
  197. if (gb_open("miles.dat")!=0)
  198.   panic(early_data_fault);
  199.     /* couldn't open |"miles.dat"| using GraphBase conventions;
  200.                  |io_errors| tells why */
  201. for (k=MAX_N-1; k>=0; k--) @<Read and store data for city |k|@>;
  202. if (gb_close()!=0)
  203.   panic(late_data_fault);
  204.     /* something's wrong with |"miles.dat"|; see |io_errors| */
  205. @ The bounds we've imposed on |north_weight|, |west_weight|, and |pop_weight|
  206. guarantee that the key value computed here will be between 0 and~$2^{31}$.
  207. @<Read and store...@>=
  208. {@+register node *p;
  209.   p=node_block+k;
  210.   p->kk=k;
  211.   if (k) p->link=p-1;
  212.   gb_string(p->name,'[');
  213.   if (gb_char()!='[') panic(syntax_error); /* out of sync in .{miles.dat} */
  214.   p->lat=gb_number(10);
  215.   if (p->lat<min_lat || p->lat>max_lat || gb_char()!=',')
  216.     panic(syntax_error+1); /* latitude data was clobbered */
  217.   p->lon=gb_number(10);
  218.   if (p->lon<min_lon || p->lon>max_lon || gb_char()!=']')
  219.     panic(syntax_error+2); /* longitude data was clobbered */
  220.   p->pop=gb_number(10);
  221.   if (p->pop<min_pop || p->pop>max_pop)
  222.     panic(syntax_error+3); /* population data was clobbered */
  223.   p->key=north_weight*(p->lat-min_lat)
  224.    +west_weight*(p->lon-min_lon)
  225.    +pop_weight*(p->pop-min_pop)+0x40000000;
  226.   @<Read the mileage data for city |k|@>;
  227.   gb_newline();
  228. }
  229. @ @d d(j,k) *(distance+(MAX_N*j+k))
  230. @<Read the mileage...@>=
  231. {
  232.   for (j=k+1; j<MAX_N; j++) {
  233.     if (gb_char()!=' ')
  234.       gb_newline();
  235.     d(j,k)=d(k,j)=gb_number(10);
  236.   }
  237. }
  238. @ Once all the nodes have been set up, we can use the |gb_linksort| routine
  239. to sort them into the desired order. This routine, which is part of
  240. the \{gb_graph} module, builds 128 lists from which the desired nodes
  241. are readily accessed in decreasing order of weight, using random numbers
  242. to break ties.
  243. We set the population to zero in every city that isn't chosen. Then
  244. that city will be excluded when edges are examined later.
  245.  
  246. @<Determine the |n| cities to use in the graph@>=
  247. {@+register node *p; /* the current node being considered */
  248.   register Vertex *v=new_graph->vertices; /* the first unfilled vertex */
  249.   gb_linksort(node_block+MAX_N-1);
  250.   for (j=127; j>=0; j--)
  251.     for (p=(node*)gb_sorted[j]; p; p=p->link) {
  252.       if (v<new_graph->vertices+n) @<Add city |p->kk| to the graph@>@;
  253.       else p->pop=0; /* this city is not being used */
  254.     }
  255. }
  256. @ Utility fields |x| and |y| for each vertex are set to coordinates that
  257. can be used in geometric computations; these coordinates are obtained by
  258. simple linear transformations of latitude and longitude (not by any
  259. kind of sophisticated polyconic projection). We will have
  260. $$0le xle5132, qquad 0le yle 3555.$$
  261. Utility field~|z| is set to the city's index number (0 to 127) in the
  262. original database. Utility field~|w| is set to the city's population.
  263. The coordinates computed here are compatible with those in the TEX/ file
  264. .{cities.texmap}. Users might want to incorporate edited copies of that file
  265. into documents that display results obtained with |miles| graphs.
  266. @.cities.texmap@>
  267. @d x_coord x.I
  268. @d y_coord y.I
  269. @d index_no z.I
  270. @d people w.I
  271. @<Add city |p->kk| to the graph@>=
  272. {
  273.   v->x_coord=max_lon-p->lon; /* |x| coordinate is complement of longitude */
  274.   v->y_coord=p->lat-min_lat;
  275.   v->y_coord+=(v->y_coord)>>1; /* |y| coordinate is 1.5 times latitude */
  276.   v->index_no=p->kk;
  277.   v->people=p->pop;
  278.   v->name=gb_save_string(p->name);
  279.   v++;
  280. }
  281. @ @(gb_miles.h@>=
  282. #define x_coord @tquad@> x.I
  283.  /* utility field definitions for the header file */
  284. #define y_coord @tquad@> y.I
  285. #define index_no @tquad@> z.I
  286. #define people @tquad@> w.I
  287. @* Arcs.  We make the distance negative in the matrix entry for an arc
  288. that is not to be included.  Nothing needs to be done in this regard
  289. unless the user has specified a maximum degree or a maximum edge length.
  290. @<Put the approp...@>=
  291. if (max_distance>0 || max_degree>0)
  292.   @<Prune unwanted edges by negating their distances@>;
  293. {@+register Vertex *u,*v;
  294.   for (u=new_graph->vertices;u<new_graph->vertices+n;u++) {
  295.     j=u->index_no;
  296.     for (v=u+1;v<new_graph->vertices+n;v++) {
  297.       k=v->index_no;
  298.       if (d(j,k)>0 && d(k,j)>0)
  299.         gb_new_edge(u,v,d(j,k));
  300.     }
  301.   }
  302. }
  303. @ @<Prune...@>=
  304. {@+register node *p;
  305.   if (max_degree==0) max_degree=MAX_N;
  306.   if (max_distance==0) max_distance=30000;
  307.   for (p=node_block; p<node_block+MAX_N; p++)
  308.     if (p->pop) { /* this city not deleted */
  309.       k=p->kk;
  310.       @<Blank out all undesired edges from city |k|@>;
  311.     }
  312. }
  313. @ Here we reuse the key fields of the nodes, storing complementary distances
  314. there instead of weights. We also let the sorting routine change the
  315. link fields. The other fields, however---especially |pop|---remain
  316. unchanged. Yes, the author knows this is a wee bit tricky, but why~not?
  317. @<Blank...@>=
  318. {@+register node *q;
  319.   register node*s=NULL; /* list of nodes containing edges from city |k| */
  320.   for (q=node_block; q<node_block+MAX_N; q++)
  321.     if (q->pop && q!=p) { /* another city not deleted */
  322.       j=d(k,q->kk); /* distance from |p| to |q| */
  323.       if (j>max_distance)
  324.         d(k,q->kk)=-j;
  325.       else {
  326.         q->key=max_distance-j;
  327.         q->link=s;
  328.         s=q;
  329.       }
  330.     }
  331.   gb_linksort(s);
  332.   /* now all the surviving edges from |p| are in the list |gb_sorted[0]| */
  333.   j=0; /* |j| counts how many edges have been accepted */
  334.   for (q=(node*)gb_sorted[0]; q; q=q->link)
  335.     if (++j>max_degree)
  336.       d(k,q->kk)=-d(k,q->kk);
  337. }
  338. @ Random access to the distance matrix is provided to users via
  339. the external function |miles_distance|. Caution: This function can be
  340. used only with the graph most recently made by |miles|, and only when
  341. the graph's |aux_data| has not been recycled, and only when the
  342. |z| utility fields have not been used for another purpose.
  343. The result might be negative when an edge has been suppressed. Moreover,
  344. we can in fact have |miles_distance(u,v)<0| when |miles_distance(v,u)>0|,
  345. if the distance in question was suppressed by the |max_degree| constraint
  346. on~|u| but not on~|v|.
  347. @p long miles_distance(u,v)
  348.   Vertex *u,*v;
  349. {
  350.   return d(u->index_no,v->index_no);
  351. }
  352. @ @(gb_miles.h@>=
  353. extern long miles_distance();
  354. @* Index. As usual, we close with an index that
  355. shows where the identifiers of \{gb_miles} are defined and used.