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);
}
}
}