⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gb_rand.w

📁 模拟器提供了一个简单易用的平台
💻 W
📖 第 1 页 / 共 2 页
字号:
  if (dist_to) {    while (nn<n) nn+=nn, kk--;    to_table=walker(n,nn,dist_to,new_graph);  }  if (gb_trouble_code) {    gb_recycle(new_graph);    panic(alloc_fault); /* oops, we ran out of memory somewhere back there */  }}@ @<Private...@>=typedef struct {  long prob; /* a probability, multiplied by $2^{31}$ and translated */  long inx; /* index that might be selected */} @[magic_entry@];@ Once the magic tables have been set up, we can generatenonuniform vertices by using the following code:@<Generate a random vertex |u|...@>={@+register magic_entry *magic;  register long uu=gb_next_rand(); /* uniform random number */  k=uu>>kk;  magic=from_table+k;  if (uu<=magic->prob) u=new_graph->vertices+k;  else u=new_graph->vertices+magic->inx;}@ @<Generate a random vertex |v|...@>={@+register magic_entry *magic;  register long uu=gb_next_rand(); /* uniform random number */  k=uu>>kk;  magic=to_table+k;  if (uu<=magic->prob) v=new_graph->vertices+k;  else v=new_graph->vertices+magic->inx;}@ So all we have to do is set up those magic tables. If |uu| is a uniformrandom integer between 0 and $2^{31}-1$, the index |k=uu>>kk| is auniform random integer between 0and |nn-1|, because of the relation between |nn| and |kk|. Once |k| iscomputed, the code above selects vertex~|k| with probability|(p+1-(k<<kk))|/$2^{31}$, where |p=magic->prob| and |magic| is the $k$thelement of the magic table; otherwise the code selectsvertex |magic->inx|. The trick is to set things up so that each vertexis selected with the proper overall probability.Let's imagine that the given distribution vector has length |nn|,instead of~|n|, by extending it if necessary with zeroes. Then theaverage entry among these |nn| integers is exactly $t=2^{30}/|nn|$.If some entry, say entry~|i|, exceeds |t|, there must be another entrythat's less than |t|, say entry~|j|. We can set the $j$th entryof the magic table so that its |prob| field selects vertex~$j$ with thecorrect probability, and so that its |inx| field equals~|i|. Thenwe are selecting vertex~|i| with a certain residual probability; so wesubtract that residual from |i|'s present probability, and repeat theprocess with vertex~|j| eliminated. The average of the remaining entriesis still~|t|, so we can repeat this procedure until all remaining entriesare exactly equal to~|t|. The rest is easy.During the calculation, we maintain two linked lists of|(prob,inx)| pairs. The |hi| list contains entries with |prob>t|,and the |lo| list contains the rest. During this part of the computationwe call these list elements `nodes', and we use the field names|key| and~|j| instead of |prob| and |inx|.@<Private...@>=typedef struct node_struct {  long key; /* a numeric quantity */  struct node_struct *link; /* the next node on the list */  long j; /* a vertex number to be selected with probability $|key|/2^{30}$ */} node;static Area temp_nodes; /* nodes will be allocated in this area */static node *base_node; /* beginning of a block of nodes */@ @<Internal...@>=static magic_entry *walker(n,nn,dist,g)  long n; /* length of |dist| vector */  long nn; /* $2^{\lceil\mskip1mu\lg n\rceil}$ */  register long *dist;    /* start of distribution table, which sums to $2^{30}$ */  Graph *g; /* tables will be allocated for this graph's vertices */{@+magic_entry *table; /* this will be the magic table we compute */  long t; /* average |key| value */  node *hi=NULL, *lo=NULL; /* nodes not yet included in magic table */  register node *p, *q; /* pointer variables for list manipulation */  base_node=gb_typed_alloc(nn,node,temp_nodes);  table=gb_typed_alloc(nn,magic_entry,g->aux_data);  if (!gb_trouble_code) {    @<Initialize the |hi| and |lo| lists@>;    while (hi) @<Remove a |lo| element and match it with a |hi| element;        deduct the residual probability from that |hi|~element@>;    while (lo) @<Remove a |lo| element of |key| value |t|@>;  }  gb_free(temp_nodes);  return table; /* if |gb_trouble_code| is nonzero, the table is empty */}@ @<Initialize the |hi| and |lo| lists@>=t=0x40000000/nn; /* this division is exact */p=base_node;while (nn>n) {  p->key=0;  p->link=lo;  p->j=--nn;  lo=p++;}for (dist=dist+n-1; n>0; dist--,p++) {  p->key=*dist;  p->j=--n;  if (*dist>t)    p->link=hi,@, hi=p;  else p->link=lo,@, lo=p;}@ When we change the scale factor from $2^{30}$ to $2^{31}$, we need tobe careful lest integer overflow occur. The introduction of register |x| intothis code removes the risk.@<Remove a |lo| element and match it with a |hi| element...@>={@+register magic_entry *r; register long x;  p=hi,@, hi=p->link;  q=lo,@, lo=q->link;  r=table+q->j;  x=t*q->j+q->key-1;  r->prob=x+x+1;  r->inx=p->j;  /* we have just given |q->key| units of probability to vertex |q->j|,     and |t-q->key| units to vertex |p->j| */  if ((p->key-=t-q->key)>t)    p->link=hi,@, hi=p;  else p->link=lo,@, lo=p;}@ When all remaining entries have the average probability, the|inx| component need not be set, because it will never be used.@<Remove a |lo| element of |key| value |t|@>={@+register magic_entry *r; register long x;  q=lo, lo=q->link;  r=table+q->j;  x=t*q->j+t-1;  r->prob=x+x+1;  /* that's |t| units of probability for vertex |q->j| */}@*Random bipartite graphs. The procedure call$$\hbox{|random_bigraph(n1,n2,m,multi,dist1,dist2,min_len,max_len,seed)|}$$is designed to produce a pseudo-random bipartite graphwith |n1| vertices in one part and |n2| in the other, having |m| edges.The remaining parameters |multi|, |dist1|, |dist2|, |min_len|, |max_len|,and |seed| have the same meaning as the analogous parameters of |random_graph|.In fact, |random_bigraph| does its work by reducing its parametersto a special case of |random_graph|. Almost all that needs to be done isto pad |dist1| with |n2| trailing zeroes and |dist2| with |n1| leadingzeroes. The only slightly tricky part occurs when |dist1| and/or |dist2| arenull, since non-null distribution vectors summing exactly to $2^{30}$ must thenbe fabricated.@<External f...@>=Graph *random_bigraph(n1,n2,m,multi,dist1,dist2,min_len,max_len,seed)  unsigned long n1,n2; /* number of vertices desired in each part */  unsigned long m; /* number of edges desired */  long multi; /* allow duplicate edges? */  long *dist1, *dist2; /* distribution of edge endpoints */  long min_len,max_len; /* bounds on random lengths */  long seed; /* random number seed */{@+unsigned long n=n1+n2; /* total number of vertices */  Area new_dists;  long *dist_from, *dist_to;  Graph *new_graph;  init_area(new_dists);  if (n1==0 || n2==0) panic(bad_specs); /* illegal options */  if (min_len>max_len) panic(very_bad_specs); /* what are you trying to do? */  if (((unsigned long)(max_len))-((unsigned long)(min_len))>=      ((unsigned long)0x80000000)) panic(bad_specs+1); /* too much range */  dist_from=gb_typed_alloc(n,long,new_dists);  dist_to=gb_typed_alloc(n,long,new_dists);  if (gb_trouble_code) {    gb_free(new_dists);    panic(no_room+2); /* no room for auxiliary distribution tables */  }  @<Compute the entries of |dist_from| and |dist_to|@>;  new_graph=random_graph(n,m,multi,0L,0L,                dist_from,dist_to,min_len,max_len,seed);  sprintf(new_graph->id,"random_bigraph(%lu,%lu,%lu,%d,%s,%s,%ld,%ld,%ld)",@|    n1,n2,m,multi>0?1:multi<0?-1:0,dist_code(dist1),dist_code(dist2),@|    min_len,max_len,seed);  mark_bipartite(new_graph,n1);  gb_free(new_dists);  return new_graph;}@ The relevant identity we need here is the replicative law for thefloor function:$$\left\lfloor x\over n\right\rfloor+\left\lfloor x+1\over n\right\rfloor+ \cdots + \left\lfloor x+n-1\over n\right\rfloor = \lfloor x\rfloor\,.$$@<Compute the entries...@>={@+register long *p, *q; /* traversers of the dists */  register long k; /* vertex count */  p=dist1; q=dist_from;  if (p)    while (p<dist1+n1) *q++=*p++;  else for (k=0; k<n1; k++) *q++=(0x40000000+k)/n1;  p=dist2; q=dist_to+n1;  if (p)    while (p<dist2+n2) *q++=*p++;  else for (k=0; k<n2; k++) *q++=(0x40000000+k)/n2;}@* Random lengths. The subroutine call$$\hbox{|random_lengths(g,directed,min_len,max_len,dist,seed)|}$$takes an existing graph and assigns new lengths toeach of its arcs. If |dist=NULL|, the lengths will be uniformly distributedbetween |min_len| and |max_len| inclusive; otherwise |dist|should be a probability distribution vector of length |max_len-min_len+1|,like those in |random_graph|.If |directed=0|, pairs of arcs $u\to v$ and $v\to u$ will be regarded asa single edge, both arcs receiving the same length.The procedure returns a nonzero value if something goes wrong; in thatcase, graph |g| will not have been changed.Alias tables for generating nonuniform random lengths will survivein |g->aux_data|.@<External f...@>=long random_lengths(g,directed,min_len,max_len,dist,seed)  Graph *g; /* graph whose lengths will be randomized */  long directed; /* is it directed? */  long min_len,max_len; /* bounds on random lengths */  long *dist; /* distribution of lengths */  long seed; /* random number seed */{@+register Vertex *u,*v; /* current vertices of interest */  register Arc *a; /* current arc of interest */  long nn=1, kk=31; /* variables for nonuniform generation */  magic_entry *dist_table; /* alias table for nonuniform generation */  if (g==NULL) return missing_operand; /* where is |g|? */  gb_init_rand(seed);  if (min_len>max_len) return very_bad_specs; /* what are you trying to do? */  if (((unsigned long)(max_len))-((unsigned long)(min_len))>=      ((unsigned long)0x80000000)) return bad_specs; /* too much range */  @<Check |dist| for validity, and set up the |dist_table|@>;  sprintf(buffer,",%d,%ld,%ld,%s,%ld)",directed?1:0,@|     min_len,max_len,dist_code(dist),seed);  make_compound_id(g,"random_lengths(",g,buffer);  @<Run through all arcs and assign new lengths@>;  return 0;}@ @<Private dec...@>=static char buffer[]="1,-1000000001,-1000000000,dist,1000000000)";@ @<Check |dist| for validity...@>=if (dist) {@+register long acc; /* sum of probabilities */  register long *p; /* pointer to current probability of interest */  register long n=max_len-min_len+1;  for (acc=0,p=dist; p<dist+n; p++) {    if (*p<0) return -1; /* negative probability */    if (*p>0x40000000-acc) return 1; /* probability too high */    acc+=*p;  }  if (acc!=0x40000000) return 2; /* probabilities don't sum to 1 */  while (nn<n) nn+=nn,kk--;  dist_table=walker(n,nn,dist,g);  if (gb_trouble_code) {    gb_trouble_code=0;    return alloc_fault; /* not enough room to generate the magic tables */  }}@ @<Run through all arcs and assign new lengths@>=for (u=g->vertices;u<g->vertices+g->n;u++)  for (a=u->arcs;a;a=a->next) {    v=a->tip;    if (directed==0 && u>v) a->len=(a-1)->len;    else {@+register long len; /* a random length */      if (dist==0) len=rand_len;      else {@+long uu=gb_next_rand();        long k=uu>>kk;        magic_entry *magic=dist_table+k;        if (uu<=magic->prob) len=min_len+k;        else len=min_len+magic->inx;      }      a->len=len;      if (directed==0 && u==v && a->next==a+1) (++a)->len=len;    }  }@* Index. Here is a list that shows where the identifiers of this program aredefined and used.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -