算法--最大流

最大流

性質

算法輸入:
圖G
源點s
匯點e
輸入限制:
G中若存在邊edge,則G中不存在邊edge.Reverse
若edge爲G中的,則有edge.m_nWeight > 0

算法目標:
計算從源點到匯點的最大流

概念:
1.最大流首先是一個可行流
2.每個可行流具備流量屬性,最大流是所有可行流中流量最大的可行流
3.對於滿足輸入限制的G,我們定義G中從s到e的一條可行流爲
從s發出一系列邊,這些邊包含流量屬性。
這些從s發出的流量,在G的邊上流動,最終通過一系列邊,流入e
準確地說即:
可行流stream是G中一系列具有流量屬性的邊的集合
滿足:
若edge爲stream中一條邊,則0 < edge.m_nStream <= edge.m_nWeight
對於G中除s,e的其他節點p,滿足 節點p流入的總流 = 節點p流出的總流

4.我們定義可行流stream的流量爲
從s流出的總流 

接口設計

template<typename Key, typename Value>
class MaxStream
{
public:
	typename typedef DataStruct::GraphStruct::Graph<Key, Value> InnerGraph;

	MaxStream(const InnerGraph& nGraph_);
	~MaxStream();
	typename InnerGraph Run(const Key& nSourceKey_, const Key& nDestKey_);

private:
	void GetAdjustGraph(const InnerGraph& nStreamGraph_, 
		const InnerGraph& nGraph_,
		InnerGraph& nAdjustGraph_);
private:
			MaxStream(const MaxStream&) = default;
			MaxStream& operator=(const MaxStream&) = default;
private:
	const InnerGraph& m_nGraph;
};

實現

構造

template<typename Key, typename Value>
MaxStream<Key, Value>::MaxStream(const InnerGraph& nGraph_)
	: m_nGraph(nGraph_)
{

}

析構

template<typename Key, typename Value>
MaxStream<Key, Value>::~MaxStream()
{

}

算法實現

template<typename Key, typename Value>
typename DataStruct::GraphStruct::Graph<Key, Value> MaxStream<Key, Value>::Run(const Key& nSourceKey_, const Key& nDestKey_)
{
	// 流圖
	InnerGraph _nStreamGraph = m_nGraph;
	DataStruct::Array::DynArray<typename InnerGraph::Edge*> _arrEdges = _nStreamGraph.GetEdgesArray();
	for (int _i = 0; _i < _arrEdges.GetSize(); _i++)
	{
		_arrEdges[_i]->m_nWeight = 0.0;
	}

	while (true)
	{
		// 構造流圖相匹配的調節圖
		DataStruct::Array::DynArray<typename InnerGraph::Node*> _arrNodes = _nStreamGraph.GetNodesArray();
		InnerGraph _nAdjustGraph;
		for (int _i = 0; _i < _arrNodes.GetSize(); _i++)
		{
			_nAdjustGraph.AddNode(_arrNodes[_i]->GetPair());
		}

		// 基於流圖,原始圖得到調節圖
		GetAdjustGraph(_nStreamGraph, m_nGraph, _nAdjustGraph);
		BreadthFirstVisit<Key, Value> _alBreathFirst(_nAdjustGraph);
		DataStruct::Array::DynArray<typename BreadthFirstVisit<Key, Value>::Node*> _arrBreadthNodes = _alBreathFirst.Run(nSourceKey_);
		BreadthFirstVisit<Key, Value>::Node* _pDest = nullptr;
		for (int _i = 0; _arrBreadthNodes.GetSize(); _i++)
		{
			if (_arrBreadthNodes[_i]->GetGraphNode() == nullptr)
			{
				throw "graph node not exist";
			}

			if (_arrBreadthNodes[_i]->GetGraphNode()->GetPair().m_nKey == nDestKey_)
			{
				_pDest = _arrBreadthNodes[_i];
				break;
			}
		}

		if (_pDest->GetPreNode() == nullptr)
		{
			break;
		}

		DataStruct::Array::DynArray<typename InnerGraph::EdgeIdentity> _arrEdgesOnPath;
		typename InnerGraph::EdgeIdentity _nEdgeIdentity;
		if (_pDest->GetPreNode()->GetGraphNode() == nullptr)
		{
			throw "pre graph node not exist";
		}

		_nEdgeIdentity.m_nStartKey = _pDest->GetPreNode()->GetGraphNode()->GetPair().m_nKey;
		if (_pDest->GetGraphNode() == nullptr)
		{
			throw "graph node not exist";
		}

		_nEdgeIdentity.m_nEndKey = _pDest->GetGraphNode()->GetPair().m_nKey;
		typename InnerGraph::Edge* _pEdge = _nAdjustGraph.SearchEdge(_nEdgeIdentity);
		if (_pEdge == nullptr)
		{
			throw "edge not exist";
		}

		double _nMinWeight = _pEdge->m_nWeight;
		while (_pDest->GetPreNode() != nullptr)
		{
			if (_pDest->GetPreNode()->GetGraphNode() == nullptr)
			{
				throw "pre graph node not exist";
			}

			_nEdgeIdentity.m_nStartKey = _pDest->GetPreNode()->GetGraphNode()->GetPair().m_nKey;
			if (_pDest->GetGraphNode() == nullptr)
			{
				throw "graph node not exist";
			}

			_nEdgeIdentity.m_nEndKey = _pDest->GetGraphNode()->GetPair().m_nKey;
			_arrEdgesOnPath.Add(_nEdgeIdentity);
			_pEdge = _nAdjustGraph.SearchEdge(_nEdgeIdentity);
			if (_pEdge == nullptr)
			{
				throw "edge not exist";
			}

			if (_pEdge->m_nWeight < _nMinWeight)
			{
				_nMinWeight = _pEdge->m_nWeight;
			}

			_pDest = _pDest->GetPreNode();
		}

		for (int _i = 0; _i < _arrEdgesOnPath.GetSize(); _i++)
		{
			_nEdgeIdentity = _arrEdgesOnPath[_i];
			_pEdge = _nStreamGraph.SearchEdge(_nEdgeIdentity);
			if (_pEdge != nullptr)
			{
				_pEdge->m_nWeight += _nMinWeight;
			}
			else
			{
				_pEdge = _nStreamGraph.SearchEdge(_nEdgeIdentity.Reverse());
				if (_pEdge != nullptr)
				{
					_pEdge->m_nWeight -= _nMinWeight;
				}
				else
				{
					throw "unexpected exception";
				}
			}
		}
	}

	return _nStreamGraph;
}

template<typename Key, typename Value>
void MaxStream<Key, Value>::GetAdjustGraph(
	const InnerGraph& nStreamGraph_,
	const InnerGraph& nGraph_,
	InnerGraph& nAdjustGraph_)
{
	DataStruct::Array::DynArray<typename InnerGraph::Edge*> _arrEdges = nStreamGraph_.GetEdgesArray();
	for (int _i = 0; _i < _arrEdges.GetSize(); _i++)
	{
		InnerGraph::EdgeIdentity _nIdentity = _arrEdges[_i]->GetIdentity();
		double _nStream = _arrEdges[_i]->m_nWeight;
		InnerGraph::Edge* _pOriginEdge = nGraph_.SearchEdge(_nIdentity);
		if (_pOriginEdge == nullptr)
		{
			throw "edge not exist";
		}

		double _nWeight = _pOriginEdge->m_nWeight;
		if (_nWeight - _nStream < 0.0)
		{
			throw "unexpected error";
		}

		if (_nWeight - _nStream > 0.0)
		{
			nAdjustGraph_.AddEdge(_nIdentity);
			InnerGraph::Edge* _pEdge = nAdjustGraph_.SearchEdge(_nIdentity);
			if (_pEdge == nullptr)
			{
				throw "edge not exist";
			}

			_pEdge->m_nWeight = _nWeight - _nStream;
		}

		if (_nStream < 0.0)
		{
			throw "unexpected error";
		}

		if (_nStream > 0.0)
		{
			nAdjustGraph_.AddEdge(_nIdentity.Reverse());
			InnerGraph::Edge* _pEdge = nAdjustGraph_.SearchEdge(_nIdentity.Reverse());
			if (_pEdge == nullptr)
			{
				throw "edge not exist";
			}

			_pEdge->m_nWeight = _nStream;
		}
	}
}

算法目標&正確性證明

算法目標分析
算法輸入:
圖G
源點s
匯點e
輸入限制:
G中若存在邊edge,則G中不存在邊edge.Reverse
若edge爲G中的,則有edge.m_nWeight > 0

算法目標:
計算從源點到匯點的一條最大流

概念:
1.最大流首先是一個可行流
2.每個可行流具備流量屬性,最大流是所有可行流中流量最大的可行流
3.對於滿足輸入限制的G,我們定義G中從s到e的一條可行流爲
從s發出一系列邊,這些邊包含流量屬性。
這些從s發出的流量,在G的邊上流動,最終通過一系列邊,流入e
準確地說即:
可行流stream是G中一系列具有流量屬性的邊的集合
滿足:
若edge爲stream中一條邊,則0 < edge.m_nStream <= edge.m_nWeight
對於G中除s,e的其他節點p,滿足 節點p流入的總流 = 節點p流出的總流

4.我們定義可行流stream的流量爲
從s流出的總流 

算法正確性證明
首先我們的算法處理過程爲:
給定G中s到e的某可行流stream
循環迭代:
基於可行流stream,構造調節圖
在調節圖中尋找s到e,節點個數最少的一條無環路徑p
若p存在,基於p對stream進行調節,得到調節後的stream
若p不存在,表示stream已經無法繼續調節。stream即爲G中s到e的一條最大流。

因爲基於迭代實現,故正確性分析中,採用循環不變式
要證明算法可以達到目標需要證明
1.循環不變式:
每次循環開始時,stream均爲可行流。且此可行流相比上一次迭代前的流量更大。
2.當迭代中,無法找到p時,表明此時stream已經是最大流

先證明循環不變式:
初始時,stream是可行流。滿足。
假設第k次迭代時,依據循環不變式,此時stream是一個可行流
首先,stream是G中邊集合的一個子集。每條邊edge有流量stream,且0 < edge.stream <= edge.weight
AdjustG是一個圖。具備和G一致的節點集合。
AdjustG中邊的原則爲:
若,edge爲stream一條邊,
若edge.weight - edge.stream > 0,則edge也爲AdjustG中一條邊。邊的權重爲 edge.weight - edge.stream。
edge.Reverse也爲AdjustG中一條邊。邊的權重爲 edge.stream。

這樣處理後,
意味着,AdjustG中我們允許對
stream某邊edge,繼續增加改變流量,或者減小改變流量來進行調節。
這和實際的流的調節過程一致。

在AdjustG中,
若我們找到一條從s到e的無環的,節點個數最小的路徑p:s->x1->...->xn->p
則,我們基於p對流stream進行調節
具體爲:
先得到路徑p上邊的集合,及改集合中邊的最小權重weight。
對該集合中每條邊edge
若該邊是stream邊集合中的邊,
則,調整edge的流量爲 edge.stream += weight。
其中,
weight <= edge.weight
edge.stream + weight <= edge.weight
若該邊不是stream邊集合中的邊,
若,edge.Reverse必是stream的邊集合中的邊
則,調節stream中邊edge.Reverse的流量爲 edge.Reverse.stream - weight
其中,
weight <= edge.weight
edge.Reverse.stream - weight >= 0

則,基於p對路徑stream進行調節後,
一方面,調節後,stream中每條邊的流量均滿足0 < stream <= 此邊在G的weight
另一方面,路徑p的邊集合,滿足
對除s,e外其他節點,流入 = 流出
對s有,流出爲 weight
將其更新到stream中後
stream邊集合仍然滿足,對於G中除s,e外節點p
滿足p的流入 = p的流出
對s,s的流出在迭代後增加了weight
綜合,通過迭代,我們更新後的stream仍然是一個可行流,且此可行流相比迭代前,流量提升了weight
循環不變式,成立。

2.證明,迭代中,無法找到p是stream已經是最大流
採用反證法
假設此時stream不是最大流
我們設最大流爲stream_max

綜合,
假設成立下,此時在AdjustG中必然存在一個邊的子集。
滿足,此邊的子集中,
對於除s,e外其餘節點p,在此邊的子集下,流入 = 流出
對於s,節點s的流出爲某個正值。
則,此時AdjustG中必然存在從s到e的路徑p
這與此時,對AdjustG運行廣度優先,無法獲取p相互矛盾
故,假設不成立
即,這種情況下stream此時,已經最優。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章