PercentileSelector.cs 2.7 KB

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