排序网络利用比较器进行简单的组合形成排序网络,利用并行方式执行,可以实现高效的排序算法,排序网络如果用软件实现,其实没有什么大的意义,因为目前的计算机虽然有多核,也可以用多线程,在一定程度上实现真正的并行,但其并行能力相对于稍具规模的排序网络来说都是有限的.排序网络用硬件来实现就非常具有优势了,排序网络的基本单元比较器的结构非常简单,适合大规模应用,还可以模块化.在组成比较排序网络后,在有限的时钟周期序列下就可以完成输入序列的排序(不需要通常系统意义下的并行).在规模为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 });