按要求自动生成无向/有向图(基于C++实现)

按照要求自动生成无向/有向图(基于C++实现)

本文内容是按照图的类型(随机有向图 无向连通图、节点个数N、边个数M、以及边的权值范围(float),来生成随机图。
其中无向图生成过程中,为了保证其连通关系,采用了从最小连通图(即两个节点)出发,逐步利用连接关系“吞并”其它连通图,最终得到一个最小连通子图,确保了生成的最终图的连通性,很具有学习意义。
详细代码我已经上传至码云:create_random_graph(基于VS2017)
生成的图信息可用Matlab显示出来,具体可参考我的博文:MATLAB读取csv文件显示有向/无向图和目标路径(用于路径搜索算法的展示)

最小连通子图生成算法

思路:

  1. 将N节点按照两个节点随机分成N/2个小组(N为奇数,最后一个小组为三个节点),分完之后,确定其连接关系(小组内相邻两节点之间生成边);
  2. 在现有的小组中随机挑选出两个小组,并在挑选出的小组中随机挑选一个节点作为连接节点,然后将两个小组连接(即编号小的小组吞并编号大的小组);
  3. 重复2,直到只有一个小组

具体实现:
设需要生成的图具有N个节点,则建立两个一维数组:
arr[N]:从1-N的随机序列,用来存储N个节点的序号
arr_id[N]:从1-N/2的顺序列,用来存储N个节点所分组的小组id

以输入N=11为例,一开始,两个数组分别为:

arr[0] arr[1] arr[2] arr[3] arr[4] arr[5] arr[6] arr[7] arr[8] arr[9] arr[10]
9 1 5 6 10 4 3 2 0 7 8
arr_id[0] arr_id[1] arr_id[2] arr_id[3] arr_id[4] arr_id[5] arr_id[6] arr_id[7] arr_id[8] arr_id[9] arr_id[10]
1 1 2 2 3 3 4 4 5 5 5

在arr_id数组中,可以看到arr数组中相邻两个为一个小组,为了确定小组内节点的连接关系,于是每一个小组都增加节点之间的边:
小组1:edge(9,1)
小组2:edge(5,6)
小组3:edge(10,4)
小组4:edge(3,2)
小组5:edge(0,7),edge(7,8)
这样就保证了所有小组内部的连通性,然后,对5个小组之间进行随机配对并连接:

  1. 选择小组2、小组3进行配对,并从小组2中挑选出节点5,从小组3中挑选出节点4,生成边edge(5,4),连接小组2和小组3,且连接过后,小组2吞并小组3,此时两个数组为:
arr[0] arr[1] arr[2] arr[3] arr[4] arr[5] arr[6] arr[7] arr[8] arr[9] arr[10]
9 1 5 6 10 4 3 2 0 7 8
arr_id[0] arr_id[1] arr_id[2] arr_id[3] arr_id[4] arr_id[5] arr_id[6] arr_id[7] arr_id[8] arr_id[9] arr_id[10]
1 1 2 2 2 2 4 4 5 5 5

此时小组数为4>1,继续;

  1. 选择小组4、小组5进行配对,并从小组4中挑选出节点2,从小组5中挑选出节点7,生成边edge(2,7),连接小组4和小组5,且连接过后,小组4吞并小组5,此时两个数组为:
arr[0] arr[1] arr[2] arr[3] arr[4] arr[5] arr[6] arr[7] arr[8] arr[9] arr[10]
9 1 5 6 10 4 3 2 0 7 8
arr_id[0] arr_id[1] arr_id[2] arr_id[3] arr_id[4] arr_id[5] arr_id[6] arr_id[7] arr_id[8] arr_id[9] arr_id[10]
1 1 2 2 2 2 4 4 4 4 4

此时小组数为3>1,继续;

  1. 选择小组2、小组4进行配对,并从小组2中挑选出节点6,从小组4中挑选出节点8,生成边edge(6,8),连接小组2和小组4,且连接过后,小组2吞并小组4,此时两个数组为:
arr[0] arr[1] arr[2] arr[3] arr[4] arr[5] arr[6] arr[7] arr[8] arr[9] arr[10]
9 1 5 6 10 4 3 2 0 7 8
arr_id[0] arr_id[1] arr_id[2] arr_id[3] arr_id[4] arr_id[5] arr_id[6] arr_id[7] arr_id[8] arr_id[9] arr_id[10]
1 1 2 2 2 2 2 2 2 2 2

此时小组数为2>1,继续;

  1. 选择小组2、小组1进行配对,并从小组2中挑选出节点4,从小组1中挑选出节点9,生成边edge(4,9),连接小组2和小组1,且连接过后,小组1吞并小组2,此时两个数组为:
arr[0] arr[1] arr[2] arr[3] arr[4] arr[5] arr[6] arr[7] arr[8] arr[9] arr[10]
9 1 5 6 10 4 3 2 0 7 8
arr_id[0] arr_id[1] arr_id[2] arr_id[3] arr_id[4] arr_id[5] arr_id[6] arr_id[7] arr_id[8] arr_id[9] arr_id[10]
1 1 1 1 1 1 1 1 1 1 1

此时小组数为1=1,结束。

算法代码

			//step1 
			//生成一个连通子图,N-1条边			
			//1.1首先随机分组
			//一个记录节点小组下标的数组
			int  *arr_id = new int[NodeCounts];
			for (int i = 0; i < NodeCounts; i++)
				arr_id[i] = 0;
			//小组id
			int id = 0;
			//产生一个随机节点序列
			int *arr = new int[NodeCounts];
			for (int i = 0; i < NodeCounts; i++)
				arr[i] = 0;
			int count = 0;
			srand(time(NULL));
			while (count < NodeCounts)
			{
   
				int val = rand() % NodeCounts;
				if (!arr[val])
				{
   
					//printf("%d ", val);
					arr[val] = count + 1;
					++count;
				}
			}
			/*for (int i = 0; i < NodeCounts; i++)
				printf("%d ", arr[i]);*/
			//1.2根据随机序列arr,两个节点一组进行分组
			int node1 = 1, node2 = 1;
			double len = 0.0;
			for (int i = 0; i <= NodeCounts - 2; i=i+2)
			{
   
				//分组,标记分组号
				id++;
				arr_id[i] = id;
				arr_id[i+1] = id;				
				//添加相应边
				node1 = arr[i] - 1;
				node2 = arr[i + 1] - 1;
				len = rand() / double(RAND_MAX) + (double)(rand() % (EdgeLengthMax - EdgeLengthMin) + EdgeLengthMin + 1);
				Edge newedge(node1,node2,len);
				newedge.print();
				graph.AddEdge(node1, node2, len);
				graph.AddEdge(node2, node1, len);
								
			}			
			if (NodeCounts % 2 == 1)//奇数,最后一个小组为三个节点
			{
   
				arr_id[NodeCounts-2] = id;
				arr_id[NodeCounts-1] = id;
				//添加相应边
				node1 = arr[NodeCounts - 2] - 1;
				node2 = arr[NodeCounts - 1] - 1;
				len = rand() / double(RAND_MAX) + (double)(rand() % (EdgeLengthMax - EdgeLengthMin) + EdgeLengthMin + 1);
				Edge newedge(node1
  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值