排序网络(C#模拟)

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


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值