排序网络(C#模拟)
2012-08-04 17:23 java线程例子 阅读(190) 评论(0) 编辑 收藏 举报排序网络利用比较器进行简单的组合形成排序网络,利用并行方式执行,可以实现高效的排序算法,排序网络如果用软件实现,其实没有什么大的意义,因为目前的计算机虽然有多核,也可以用多线程,在一定程度上实现真正的并行,但其并行能力相对于稍具规模的排序网络来说都是有限的.排序网络用硬件来实现就非常具有优势了,排序网络的基本单元比较器的结构非常简单,适合大规模应用,还可以模块化.在组成比较排序网络后,在有限的时钟周期序列下就可以完成输入序列的排序(不需要通常系统意义下的并行).在规模为n的排序网络中,序列长度1-n的序列排序用时是一样的.当然,排序网络用硬件实现的一个缺点就是排序规模n扩展比较困难.对于现代的计算机,如果内置一个排序网络模块(规模为2^32),成本也不是很高,而且还可以利用软件在此基础上再做归并排序,对于亿级的序列规模排序性能可以极大地改善.
下面的算法只是模拟一下(虽然可以模拟并行,但感觉意义不大,这里只是体会一下算法原理的实现):
/// <summary> /// 输入事件,用于触发比较器的输入管道有输入的事件 /// </summary> public delegate void InputEvent(); /// <summary> /// 比较器的输入输出管道 /// </summary> public class PipeLine { /// <summary> /// 管道值 /// </summary> public int Value { get; set; } /// <summary> /// 下一节管道线 /// </summary> public PipeLine NextPipe { get; set; } /// <summary> /// 输入触发事件,这里用委托,不用事件,因为挂接到该事件的对象小于等于1 /// </summary> private InputEvent _InputEvent; /// <summary> /// 管道构造,需传入管道有输入时的事件处理 /// </summary> /// <param name="InputEvent"></param> public PipeLine(InputEvent InputEvent) { this._InputEvent = InputEvent; } /// <summary> /// 管道传输值 /// </summary> /// <param name="AValue">值</param> public void TransferValue(int AValue) { this.Value = AValue; //如果有下一个管道,则传值给下去 if (NextPipe != null) { NextPipe.TransferValue(AValue); } //触发管道有值输入事件,这段代码是我利用触发机制实现时用的. if (_InputEvent != null) { _InputEvent(); } } /// <summary> /// 管道传输值 /// </summary> public void TransferValue() { TransferValue(this.Value); } } /// <summary> /// 比较器,由两个输入管道和两个输出管道加比较元件构成 /// </summary> public class Comparator { private int OperandCount = 0; /// <summary> /// 输入管道A /// </summary> public PipeLine InputA { get; private set; } /// <summary> /// 输入管道B /// </summary> public PipeLine InputB { get; private set; } /// <summary> /// 输出管道A /// </summary> public PipeLine OutputA { get; private set; } /// <summary> /// 输出管道B /// </summary> public PipeLine OutputB { get; private set; } private CompareNetwork _comparenetwork; /// <summary> /// 重新设置输入计数 /// </summary> private void Reset() { OperandCount = 0; } /// <summary> /// 构造函数,需传入比较网络(本身可以不传,这里主要是为了做 /// 一些测试,所以会把整个网络引用传入) /// </summary> /// <param name="ACompareNets">比较网络</param> public Comparator(CompareNetwork ACompareNets) { this._comparenetwork = ACompareNets; //InputA = new PipeLine(new InputEvent(DoCompare)); //InputB = new PipeLine(new InputEvent(DoCompare)); //不采用事件触发,而模拟脉冲方式 InputA = new PipeLine(null); InputB = new PipeLine(null); OutputA = new PipeLine(null); OutputB = new PipeLine(null); Reset(); } /// <summary> /// 进行比较动作 /// </summary> public void DoCompare() { OperandCount++; //只有两个输入管道都有值的时候才进行真正比较. //if (OperandCount <= 1) //{ // return; //} if (InputA.Value > InputB.Value) { OutputA.Value = InputB.Value; OutputB.Value = InputA.Value; } else { OutputA.Value = InputA.Value; OutputB.Value = InputB.Value; } OutputA.TransferValue(); OutputB.TransferValue(); Reset(); this._comparenetwork.CompareTimes++; } } /// <summary> /// 比较排序网络,这里只是模拟算法,没有实现真正意义上的并行比较, /// 用软件实现这种比较网络意义不是很大,用专门的硬件实现才会发挥其 /// 真正的优势。 /// </summary> public class CompareNetwork { public int CompareTimes { get; set; } private int _n { get; set; } private int _k { get; set; } private List<PipeLine> _inputPipeLines; private List<PipeLine> _outputPipeLines; private List<Comparator> _comparators; private List<List<Comparator>> _comparatorMatirx; private PipeLine[] _outPuts; /// <summary> /// 比较网络构造函数 /// </summary> /// <param name="k">比较网络规模基数,比较输入规模n=2^k</param> public CompareNetwork(int k) { this._n = Convert.ToInt32(Math.Pow(2,k)); this._k = k; _inputPipeLines = new List<PipeLine>(); _outputPipeLines = new List<PipeLine>(); _comparatorMatirx = new List<List<Comparator>>(); for (int i = 0; i < this._n; i++) { _inputPipeLines.Add(new PipeLine(null)); _outputPipeLines.Add(new PipeLine(null)); } _comparators = new List<Comparator>(); BuildCompareNets(); } /// <summary> /// 比较网络比较排序。 /// </summary> /// <param name="values">要排序的值集合</param> /// <returns>排好序的值集合</returns> public int[] Docompare(int[] values) { CompareTimes = 0; //输入比较的序列个数必须小于或等于比较网络的能力n if (values.Length > this._n) { return null; } int theCount = 0; //从输入管道传入值 for (int i = 1; i <= values.Length; i++) { _inputPipeLines[i - 1].TransferValue(values[i - 1]); theCount++; } //如果输入序列个数不够,则用最大值填充管道. for (int i = theCount + 1; i <= this._n; i++) { _inputPipeLines[i - 1].TransferValue(int.MaxValue); } //开始计算,按照比较器的位置从前到后执行比较,同一深度的比较器可在同一个时钟脉冲下并行完成计算,当然,这里只是模拟. for (int i = 0; i < _comparatorMatirx.Count; i++) { for (int j = 0; j < _comparatorMatirx[j].Count; j++) { _comparatorMatirx[i][j].DoCompare(); } } //从输出管道中获取值。 List<int> theRet = new List<int>(); for (int i = 0; i < values.Length; i++) { if (i < _outPuts.Length) { theRet.Add(_outPuts[i].Value); } } return theRet.ToArray(); } /// <summary> /// 构造比较网络 /// </summary> private void BuildCompareNets() { _comparatorMatirx.Clear(); PipeLine[] theInputLines = _inputPipeLines.ToArray(); for (int i = 1; i <= this._k; i++) { int theStartLevel = _comparatorMatirx.Count; theInputLines = BuildMergingNetwork(theInputLines, i, theStartLevel); } _outPuts = theInputLines; } /// <summary> /// 递归构造Bitonic排序器 /// </summary> /// <param name="InputLines">输入管道</param> /// <returns>网络输出管道集合</returns> private List<PipeLine> BuildBitonicSorter(PipeLine[] InputLines, int level) { PipeLine[] OutputLines = new PipeLine[InputLines.Length]; List<PipeLine> thePs = new List<PipeLine>(); if (InputLines.Length <= 1) { thePs.AddRange(InputLines); return thePs; } int theHalf = InputLines.Length / 2; for (int j = 1; j <= theHalf; j++) { Comparator theComparator = new Comparator(this); int theComIndex1 = j; int theComIndex2 = theComIndex1 + theHalf; InputLines[theComIndex1 - 1].NextPipe = theComparator.InputA; InputLines[theComIndex2 - 1].NextPipe = theComparator.InputB; OutputLines[theComIndex1 - 1] = theComparator.OutputA; OutputLines[theComIndex2 - 1] = theComparator.OutputB; _comparators.Add(theComparator); AddComparator(theComparator, level); } List<PipeLine> thePs_1 = new List<PipeLine>(); List<PipeLine> thePs_2 = new List<PipeLine>(); for (int j = 1; j <= theHalf; j++) { thePs_1.Add(OutputLines[j - 1]); thePs_2.Add(OutputLines[j + theHalf - 1]); } var theRet1 = BuildBitonicSorter(thePs_1.ToArray(),level+1); var theRet2 = BuildBitonicSorter(thePs_2.ToArray(),level+1); thePs.AddRange(theRet1); thePs.AddRange(theRet2); return thePs; } private void AddComparator(Comparator Comparator, int Level) { if (Level >= this._comparatorMatirx.Count) { this._comparatorMatirx.Add(new List<Comparator>()); } this._comparatorMatirx[Level].Add(Comparator); } /// <summary> /// 构造合并网络 /// </summary> /// <param name="InputLines">输入管道</param> /// <param name="k">阶段k</param> /// <param name="level">比较器所在的树型层次</param> /// <returns>网络输出管道集合</returns> private PipeLine[] BuildMergingNetwork(PipeLine[] InputLines, int k,int level) { int theNum = Convert.ToInt32(Math.Pow(2,k)); int theMergerCount = this._n / theNum; int theMergerWidth = theNum; PipeLine[] theInputLines = InputLines; PipeLine[] theOutputPipeLines_temp = new PipeLine[this._n]; for (int i = 1; i <= theMergerCount; i++) { int theStart = (i-1) * theMergerWidth + 1; int theEnd = i * theMergerWidth; int theHalfN = theMergerWidth / 2; for (int j = 1; j <= theHalfN; j++) { Comparator theComparator = new Comparator(this); int theComIndex1 =theStart + j-1; int theComIndex2 =theEnd-j+1; theInputLines[theComIndex1 - 1].NextPipe = theComparator.InputA; theInputLines[theComIndex2 - 1].NextPipe = theComparator.InputB; theOutputPipeLines_temp[theComIndex1 - 1] = theComparator.OutputA; theOutputPipeLines_temp[theComIndex2 - 1] = theComparator.OutputB; _comparators.Add(theComparator); AddComparator(theComparator, level); } //生成n/2个bitonicsorter //首先生成半清洁器 var theInputs_1 = new List<PipeLine>(); var theInputs_2 = new List<PipeLine>(); for (int j = 1; j <= theHalfN; j++) { theInputs_1.Add(theOutputPipeLines_temp[theStart + j - 2]); } for (int j = theHalfN+1; j <= theMergerWidth; j++) { theInputs_2.Add(theOutputPipeLines_temp[theStart + j - 2]); } var theOuts1 = BuildBitonicSorter(theInputs_1.ToArray(), level + 1); var theOuts2 = BuildBitonicSorter(theInputs_2.ToArray(), level + 1); for (int j = 1; j <= theHalfN; j++) { theOutputPipeLines_temp[theStart + j - 2] = theOuts1[j - 1]; } for (int j = theHalfN + 1; j <= theMergerWidth; j++) { theOutputPipeLines_temp[theStart + j - 2] = theOuts2[j - 1 - theHalfN]; } } return theOutputPipeLines_temp; } }
PS:从上面的代码也可以看出,用软件实现这种排序网络,是没有什么优势的,但非常适合用硬件来实现,
下面是测试代码:
CompareNetwork theC = new CompareNetwork(3); var theRet1 = theC.Docompare(new int[] { 1, 2, 3, 4, 4, 5, 6, 8 }); var theRet2 = theC.Docompare(new int[] { 2, 1, 3, 4 }); var theRet3 = theC.Docompare(new int[] { 3, 4, 2, 1 }); var theRet4 = theC.Docompare(new int[] { 3, 2, 4, 1 });