PercentileSelector.cs 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace Masuit.Tools.Maths;
  5. /// <summary>
  6. /// 百分位元素选择器
  7. /// </summary>
  8. public static class PercentileSelector
  9. {
  10. private static T QuickSelect<T>(IList<T> arr, int left, int right, int k) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
  11. {
  12. while (true)
  13. {
  14. if (left == right)
  15. {
  16. return arr[left];
  17. }
  18. var pivotIndex = left + (right - left) / 2;
  19. pivotIndex = Partition(arr, left, right, pivotIndex);
  20. if (k == pivotIndex)
  21. {
  22. return arr[k];
  23. }
  24. if (k < pivotIndex)
  25. {
  26. right = pivotIndex - 1;
  27. continue;
  28. }
  29. left = pivotIndex + 1;
  30. }
  31. }
  32. private static int Partition<T>(IList<T> arr, int left, int right, int pivotIndex) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
  33. {
  34. var pivotValue = arr[pivotIndex];
  35. Swap(arr, pivotIndex, right);
  36. var storeIndex = left;
  37. for (int i = left; i < right; i++)
  38. {
  39. if (arr[i].CompareTo(pivotValue) == -1)
  40. {
  41. Swap(arr, i, storeIndex);
  42. storeIndex++;
  43. }
  44. }
  45. Swap(arr, storeIndex, right);
  46. return storeIndex;
  47. }
  48. private static void Swap<T>(IList<T> arr, int i, int j)
  49. {
  50. (arr[i], arr[j]) = (arr[j], arr[i]);
  51. }
  52. /// <summary>
  53. /// 查找第percentile百分位的元素
  54. /// </summary>
  55. /// <param name="arr"></param>
  56. /// <param name="percentile"></param>
  57. public static T Percentile<T>(this IList<T> arr, double percentile) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
  58. {
  59. if (arr.Count == 0)
  60. {
  61. return default;
  62. }
  63. var k = (int)Math.Ceiling(arr.Count * percentile * 0.01) - 1;
  64. return QuickSelect(arr, 0, arr.Count - 1, k);
  65. }
  66. /// <summary>
  67. /// 查找第percentile百分位的元素
  68. /// </summary>
  69. /// <param name="arr"></param>
  70. /// <param name="percentile"></param>
  71. public static T Percentile<T>(this IEnumerable<T> arr, double percentile) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
  72. {
  73. return Percentile(arr.ToList(), percentile);
  74. }
  75. }