using System; using System.Collections.Generic; using System.Linq; namespace Masuit.Tools.Maths { /// /// 百分位元素选择器 /// public static class PercentileSelector { private static T QuickSelect(IList arr, int left, int right, int k) where T : struct, IComparable, IFormattable, IConvertible, IComparable, IEquatable { while (true) { if (left == right) { return arr[left]; } var pivotIndex = left + (right - left) / 2; pivotIndex = Partition(arr, left, right, pivotIndex); if (k == pivotIndex) { return arr[k]; } if (k < pivotIndex) { right = pivotIndex - 1; continue; } left = pivotIndex + 1; } } private static int Partition(IList arr, int left, int right, int pivotIndex) where T : struct, IComparable, IFormattable, IConvertible, IComparable, IEquatable { var pivotValue = arr[pivotIndex]; Swap(arr, pivotIndex, right); var storeIndex = left; for (int i = left; i < right; i++) { if (arr[i].CompareTo(pivotValue) == -1) { Swap(arr, i, storeIndex); storeIndex++; } } Swap(arr, storeIndex, right); return storeIndex; } private static void Swap(IList arr, int i, int j) { (arr[i], arr[j]) = (arr[j], arr[i]); } /// /// 查找第percentile百分位的元素 /// /// /// public static T Percentile(this IList arr, double percentile) where T : struct, IComparable, IFormattable, IConvertible, IComparable, IEquatable { if (arr.Count == 0) { return default; } var k = (int)Math.Ceiling(arr.Count * percentile * 0.01) - 1; return QuickSelect(arr, 0, arr.Count - 1, k); } /// /// 查找第percentile百分位的元素 /// /// /// public static T Percentile(this IEnumerable arr, double percentile) where T : struct, IComparable, IFormattable, IConvertible, IComparable, IEquatable { return Percentile(arr.ToList(), percentile); } } }