最短路径和矩阵乘法

介绍

    这里讲的顶点对之间的最短路径是基于动态规划在图中的实现。每一个循环都类似矩阵乘法,因此这个算法看起来就像是一直在做矩阵乘法。

实现

    在这里我们用邻接矩阵表示法来表示一个图,因为相对邻接表来说,邻接矩阵表示要容易些,并且采用自底而下的算法来计算最短路径权重。

typedef int (*Metrix)[VERTEX_NUMBER];

void printfMatrix(Metrix graphmetrix)
{
	for (int x = 0; x < VERTEX_NUMBER; ++ x)
	{
		for (int y = 0; y < VERTEX_NUMBER; ++ y)
		{
			cout << "\t" << graphmetrix[x][y] << " ";
		}
		cout << endl;
	}
	cout << endl;
}

    这是输出一个图的矩阵表示法,其中的每个值表示当中两个顶点之间的权重值。

int getSentinel()
{
	return 0x7fffffff;
}

Metrix entendShortestPaths(Metrix left, Metrix right)
{
	const int n = VERTEX_NUMBER;
	int *graph = new int[n * n];
	Metrix c = (int(*)[n])graph;

	for (int i = 0; i < n; ++ i)
	{
		for (int j = 0; j < n; ++ j)
		{
			c[i][j] = getSentinel();
			for (int k = 0; k < n; ++ k)
			{
				if (getSentinel() != left[i][k] && getSentinel() != right[k][j])
				{
					c[i][j] = std::min(c[i][j], left[i][k] + right[k][j]);
				}
			}
		}
	}

	return c;
}

    在计算时,我们首先把结果矩阵里面的值均初始化为最大,然后再计算它和当前两个结点权重之间的最小值,返回的最小值就是两个顶点之间的最小权重。在这里,left[i][k]就是结点i->k的最短路径值,然后,right[k][j]就是k->j的最小权重值,把2个相加就是i->j的最小权重值。接着来看看矩阵乘法的算法:

Metrix squareMatrixMultiply(Metrix left, Metrix right)
{
	const int n = VERTEX_NUMBER;
	int *graph = new int[n * n];
	Metrix c = (int(*)[n])graph;

	for (int i = 0; i < n; ++ i)
	{
		for (int j = 0; j < n; ++ j)
		{
			c[i][j] = 0;
			for (int k = 0; k < n; ++ k)
			{
				c[i][j] += left[i][k] * right[k][j];
			}
		}
	}

	return c;
}

    是不是和上面的算法很相似呢。这里计算c[i][j]的值就是left矩阵的第i行的值乘以right的第j列的所有值的结果的总和。

    然后,计算最短路径有2种方法,慢速的和快速的,其中快速的是在慢速的基础上做优化。先来看看慢速的如何计算:

Metrix slowAllPairsShortestPaths(Metrix graph)
{
	const int n = VERTEX_NUMBER;
	int *tmp = new int[n * n];
	Metrix c = (int(*)[n])tmp;
	memcpy(c, graph, n * n * sizeof(4));

	for (int m = 1; m < n - 1; ++ m)
	{
		Metrix p = entendShortestPaths(c, graph);
		delete [] c;
		c = p;
		printfMatrix(c);
	}

	return c;
}

    我们在每次扩展最短路径的结果中,把返回的结果矩阵继续和原始矩阵做扩展,直到计算n-1次为止。而在这个算法上做的优化则是:因为我们的目的只是需要得出n-1次的最后结果,而不关心中间结果。所以我们可以每次进行m*2操作来让m的值进行递增,而不是m+1。

Metrix fasterAllPairsShortestPaths(Metrix graph)
{
	const int n = VERTEX_NUMBER;
	int *tmp = new int[n * n];
	Metrix c = (int(*)[n])tmp;
	memcpy(c, graph, n * n * sizeof(4));

	int m = 1;
	while (m < n - 1)
	{
		Metrix p = entendShortestPaths(c, c);
		delete [] c;
		c = p;
		printfMatrix(c);
		m *= 2;
	}

	return c;
}

    我们可以来看看最短路径的图解过程:

慢速最短路径图解过程

慢速最短路径图解过程

    而我们再来测试下慢速和快速的运行结果,也使用上图中一样的图:

int _tmain(int argc, _TCHAR* argv[])
{
	copyright();
	cout << getSentinel() << endl;

	cout << "directed adjacency matrix: " << endl;
	int graphmetrix[VERTEX_NUMBER][VERTEX_NUMBER] = 
	{
		{0, 3, 8, 0x7fffffff, -4},
		{0x7fffffff, 0, 0x7fffffff, 1, 7},
		{0x7fffffff, 4, 0, 0x7fffffff, 0x7fffffff},
		{2, 0x7fffffff, -5, 0, 0x7fffffff},
		{0x7fffffff, 0x7fffffff, 0x7fffffff, 6, 0},
	};
	cout << "directed weight graph structure: " << endl;
	for (int x = 0; x < VERTEX_NUMBER; ++ x)
	{
		cout << "vertex " << x + 1 << ";";
		for (int y = 0; y < VERTEX_NUMBER; ++ y)
		{
			if (0 != graphmetrix[x][y])
			{
				cout << y + 1 << " ";
			}
		}
		cout << endl;
	}
	cout << "origin: " << endl;
	printfMatrix(graphmetrix);
	cout << "slow algorithm: " << endl;
	Metrix c = slowAllPairsShortestPaths(graphmetrix);
	delete [] c;
	cout << "faster algorithm: " << endl;
	c = fasterAllPairsShortestPaths(graphmetrix);
	delete [] c;

	return 0;
}
慢速最短路径运行结果

慢速最短路径运行结果

快速最短路径运行结果

快速最短路径运行结果

    从结果中可以看出,快速的比慢速的要快,而且这是在结点数量少的情况,如果是结点数量多的情况,优化效果是很显示的,计算量要少很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Z小偉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值