算法--区间数据计算

最近一年多来,一直比较忙,最近一段时间终于空闲了,把以前没写的都补上.....

这边随笔主要是计算一系列数据的间隔数据。从一堆数据中查询出每个区间的起始数据,结束数据以及数据个数,同时可以设置相应精度(小数位数)。

区间数据数据结构

 1、区间数据主要包括当前区间的起始数据,结束数据以及数据个数。结构如下:

    public struct IntervalData<TKey, TValue>
    {
        private TKey _startValue;
        private TKey _endValue;
        private TValue _count;

        public IntervalData(TKey startValue, TKey endValue, TValue count)
        {
            this._startValue = startValue;
            this._endValue = endValue;
            this._count = count;
        }

        public TKey StartValue
        {
            get { return this._startValue; }
            set { this._startValue = value; }
        }

        public TKey EndValue
        {
            get { return this._endValue; }
            set { this._endValue = value; }
        }

        public TValue Count
        {
            get { return this._count; }
            set { this._count = value; }
        }
    }

区间数据计算算法

 首先需要注意的几点如下:

1、区间应该大于等于1,精度必须小于等于15(double精度最大值)。

2、区间宽度需要微调,相应需要增加相对应的精度值。

3、最大值和最小值需要微调,相应需要增加或者减少相对应的精度值。

    public class DataCalculator
    {
        public int IntervalCount { get; set; }

        public double IntervalWidth { get; private set; }

        public double MaxValue { get; set; }

        public double MinValue { get; private set; }

        public const int MAX_DIGIT_SCALE = 15;

        public DataCalculator()
        {

        }

        public DataCalculator(int intervalCount)
        {
            if (intervalCount <= 0)
            {
                this.IntervalCount = 1;
            }
            else
            {
                this.IntervalCount = intervalCount;
            }
        }

        /// <summary>
        /// 计算间隔数据起始点,结束点以及数量的列表。
        /// </summary>
        /// <param name="values">需要计算的数值列表。</param>
        /// <param name="digits">小数点位数。用于精确到指定位数的小数点。
        /// 大于等于0,小于等于15。小于0时设置为0,大于15时设置为15。</param>
        /// <returns>返回间隔数据列表。</returns>
        public IList<IntervalData<double, int>> Calculate(IList<double> values, int digits = 0)
        {
            if (values == null || values.Count == 0)
            {
                return new List<IntervalData<double, int>>();
            }

            CheckDoubleScale(ref digits);
            AdjustMinAndMaxValue(values, digits);
            AdjustIntervalWidth(digits);

            return CalculateResult(values, digits);
        }

        private IList<IntervalData<double, int>> CalculateResult(IEnumerable<double> values, int digits)
        {
            var dataResult = new List<IntervalData<double, int>>();
            double startValue = this.MinValue;

            for (int index = 0; index < this.IntervalCount; index++)
            {
                int count = 0;
                double endValue = Math.Round(startValue + this.IntervalWidth, digits);

                foreach (double currValue in values)
                {
                    if (currValue >= startValue &&
                        currValue < endValue)
                    {
                        ++count;
                    }
                }

                if (index == this.IntervalCount - 1 && this.MaxValue < endValue)
                {
                    this.MaxValue = endValue;
                }

                dataResult.Add(new IntervalData<double, int>(startValue, endValue, count));
                startValue = endValue;
            }

            return dataResult;
        }

        private void AdjustIntervalWidth(int digits)
        {
            double intervalWidth = (this.MaxValue - this.MinValue) / this.IntervalCount;
            double currentIntervalWidth = Math.Round(intervalWidth, digits);

            if (currentIntervalWidth < intervalWidth)
            {
                currentIntervalWidth += 1 / Math.Pow(10, digits);
            }

            if (currentIntervalWidth == 0)
            {
                currentIntervalWidth = 1;
            }

            this.IntervalWidth = currentIntervalWidth;
        }

        private void AdjustMinAndMaxValue(IEnumerable<double> values, int digits)
        {
            double minValue = values.Min();
            double maxValue = values.Max();

            // 计算最小值,将最小值减少相应的精度值,避免最小值未进入计算
            double currentMinValue = Math.Round(minValue, digits);

            if (currentMinValue > minValue)
            {
                currentMinValue -= 1 / Math.Pow(10, digits);
            }

            // 计算最大值,将最大值增加相应的精度值,避免最大值未进入计算
            double currentMaxValue = Math.Round(maxValue, digits);

            if (currentMaxValue <= maxValue)
            {
                currentMaxValue += 1 / Math.Pow(10, digits);
            }

            this.MinValue = currentMinValue;
            this.MaxValue = currentMaxValue;
        }

        private static void CheckDoubleScale(ref int digits)
        {
            if (digits < 0)
            {
                digits = 0;
            }

            if (digits > MAX_DIGIT_SCALE)
            {
                digits = MAX_DIGIT_SCALE;
            }
        }
    }

具体应用

应用比较简单,示例如下:

            IList<double> dataPoints = new List<double>() { -4, 5, 6, 99.54, 0, 65 };

            var calculator = new DataCalculator(5);
            IList<IntervalData<double, int>> datas = calculator.Calculate(dataPoints, 2);

            StringBuilder builder = new StringBuilder();

            foreach (var data in datas)
            {
                builder.AppendLine(string.Format("StartValue:{0}  EndValue:{1}  Count:{2}", data.StartValue, data.EndValue, data.Count));
            }

            string result = builder.ToString();
            Console.Write(result);

输出结果为:

StartValue:-4  EndValue:16.71  Count:4
StartValue:16.71  EndValue:37.42  Count:0
StartValue:37.42  EndValue:58.13  Count:0
StartValue:58.13  EndValue:78.84  Count:1
StartValue:78.84  EndValue:99.55  Count:1

可以将该返回数据用于相关图形进行绑定以及显示。

posted @ 2013-07-13 12:13  jasen.kin  阅读(8583)  评论(2编辑  收藏  举报