虽然这可能看起来像家庭作业,但我向你保证不是。这源于我做过的一些家庭作业。
如果每个顶点的度数恰好为3,那么让我们调用一个没有自我边缘“立方”的无向图。给定正整数N我想在N个顶点上生成随机立方图。我希望它具有统一的概率,也就是说,如果N个顶点上有M个立方图,则生成每个顶点的概率为1 / M.一个较弱的条件仍然很好,每个立方图都有非零概率。
我感觉有一种快速而聪明的方法可以做到这一点,但到目前为止,我一直没有成功。
我是一个糟糕的程序员,请忍受这个糟糕的代码:
PRE:edges =(3 *个节点)/ 2,节点是偶数,选择的常数是哈希工作的方式(BIG_PRIME大于边缘,SMALL_PRIME大于节点,LOAD_FACTOR很小)。 / p>
void random_cubic_graph() {
int i, j, k, count;
int *degree;
char guard;
count = 0;
degree = (int*) calloc(nodes, sizeof(int));
while (count < edges) {
/* Try a new edge at random */
guard = 0;
i = rand() % nodes;
j = rand() % nodes;
/* Checks if it is a self-edge */
if (i == j)
guard = 1;
/* Checks that the degrees are 3 or less */
if (degree[i] > 2 || degree[j] > 2)
guard = 1;
/* Checks that the edge was not already selected with an hash */
k = 0;
while(A[(j + k*BIG_PRIME) % (LOAD_FACTOR*edges)] != 0) {
if (A[(j + k*BIG_PRIME) % (LOAD_FACTOR*edges)] % SMALL_PRIME == j)
if ((A[(j + k*BIG_PRIME) % (LOAD_FACTOR*edges)] - j) / SMALL_PRIME == i)
guard = 1;
k++;
}
if (guard == 0)
A[(j + k*BIG_PRIME) % (LOAD_FACTOR*edges)] = hash(i,j);
k = 0;
while(A[(i + k*BIG_PRIME) % (LOAD_FACTOR*edges)] != 0) {
if (A[(i + k*BIG_PRIME) % (LOAD_FACTOR*edges)] % SMALL_PRIME == i)
if ((A[(i + k*BIG_PRIME) % (LOAD_FACTOR*edges)] - i) / SMALL_PRIME == j)
guard = 1;
k++;
}
if (guard == 0)
A[(i + k*BIG_PRIME) % (LOAD_FACTOR*edges)] = hash(j,i);
/* If all checks were passed, increment the count, print the edge, increment the degrees. */
if (guard == 0) {
count++;
printf("%d\t%d\n", i, j);
degree[i]++;
degree[j]++;
}
}
问题是必须选择的最终边缘可能是自我边缘。当N-1个顶点已经是3级时,就会发生这种情况,只有1个具有1级。因此算法可能不会终止。而且,我并不完全相信概率是一致的。
我的代码可能有很多改进,但你能建议一个更好的算法来实现吗?
答案 0 :(得分:10)
假设N是偶数。 (否则N个顶点上不能有立方图)。
您可以执行以下操作:
取3N分并将它们分成N组,每组3分。
现在随机配对这些3N点(注意:3N是偶数)。即随机结束两点并形成3N / 2婚姻。)
如果组i和组j之间存在配对,则在i和j之间创建一条边。这给出了N个顶点的图形。
如果此随机配对不会创建任何多个边或循环,则您有一个立方图。
如果没有再试一次。这在预期的线性时间内运行并产生均匀分布。
注意:N个顶点上的所有立方图都是通过这种方法生成的(回应Hamish的评论)。
要看到这个:
设G是N个顶点上的立方图。
让顶点为1,2,... N。
让j的三个邻居为A(j),B(j)和C(j)。
对于每个j,构造有序对{(j,A(j)),(j,B(j)),(j,C(j))}的组。
这给了我们3N有序对。我们将它们配对:(u,v)与(v,u)配对。
因此,任何立方图对应配对,反之亦然......
有关此算法和更快算法的更多信息,请访问:Generating Random Regular Graphs Quickly。
答案 1 :(得分:2)
警告:我在这个答案中提出了许多直观但可能错误的说法。如果你打算使用这个想法,你一定要证明它们。
枚举立方图
在处理随机选择时,一个好的起点是弄清楚如何枚举所有可能的元素。这可能会揭示一些结构,并引导您进行算法。
这是我枚举立方图的策略:选择第一个顶点,并迭代三个相邻顶点的所有可能选择。在这些迭代期间,递归到下一个顶点,但需要注意每个顶点度数达到3所需的边数。继续以这种方式直到达到最低级别。现在你有了第一个立方图。撤消最近添加的边缘并继续下一个可能性,直到没有剩下。您需要考虑一些实施细节,但通常是直截了当的。
将枚举概括为选择
一旦你可以枚举所有元素,做出随机选择是微不足道的。例如,您可以扫描列表一次以计算其大小,然后选择[0,大小]中的随机数,然后再次扫描序列以获取该偏移处的元素。这是非常低效的,在最小时间内与O(n ^ 3)立方图的数量成比例,但它起作用。
牺牲统一的效率概率
这里显而易见的加速是在每个级别进行随机边缘选择,而不是迭代每种可能性。不幸的是,这将有利于某些图表,因为您的早期选择会影响后续选择的可用性。考虑到跟踪剩余自由顶点的需要,您应该能够实现O(n log n)时间和O(n)空间。明显优于枚举算法。
<强> ... 强>
可能做得更好。可能好多了。但这应该让你开始。
答案 2 :(得分:1)
cubic graph 的另一个术语是 3 - regular graph 或三价图。
你的问题需要更多的澄清,因为“立方图的数量”可能意味着2 n个节点上彼此非同构的立方图的数量或(非)的数量在2 n个标记的节点上的 - 同构的)立方图。前者由整数序列A005638给出,并且有效地均匀地选择立方图的随机同构类(即不将它们全部列出然后选择一个类)可能是一个非平凡的问题。后者由A002829给出。
维基百科上有一篇关于random regular graphs的文章,你应该看一下。