算法--區間數據計算


最近一年多來,一直比較忙,最近一段時間終於空閑了,把以前沒寫的都補上.....

這邊隨筆主要是計算一系列數據的間隔數據。從一堆數據中查詢出每個區間的起始數據,結束數據以及數據個數,同時可以設置相應精度(小數位數)。

區間數據數據結構

 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

可以將該返回數據用於相關圖形進行綁定以及顯示。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM