J'ai besoin d'écrire une fonction qui accepte un tableau de décimales et qui trouve la médiane.
Existe-t-il une fonction dans la bibliothèque mathématique de .net ?
J'ai besoin d'écrire une fonction qui accepte un tableau de décimales et qui trouve la médiane.
Existe-t-il une fonction dans la bibliothèque mathématique de .net ?
Il semble que les autres réponses utilisent le tri. Ce n'est pas optimal du point de vue des performances car il faut O(n logn)
temps. Il est possible de calculer la médiane en O(n)
temps à la place. La version généralisée de ce problème est connue sous le nom de "statistique d'ordre n", ce qui signifie qu'il faut trouver un élément K dans un ensemble tel que nous avons n éléments inférieurs ou égaux à K et le reste est supérieur ou égal à K. Ainsi, la statistique d'ordre 0 serait l'élément minimal dans l'ensemble (Remarque : certains ouvrages utilisent l'indice de 1 à N au lieu de 0 à N-1). La médiane est simplement (Count-1)/2
-statistique d'ordre.
Voici le code adopté à partir de Introduction aux algorithmes par Cormen et al, 3e édition .
/// <summary>
/// Partitions the given list around a pivot element such that all elements on left of pivot are <= pivot
/// and the ones at thr right are > pivot. This method can be used for sorting, N-order statistics such as
/// as median finding algorithms.
/// Pivot is selected ranodmly if random number generator is supplied else its selected as last element in the list.
/// Reference: Introduction to Algorithms 3rd Edition, Corman et al, pp 171
/// </summary>
private static int Partition<T>(this IList<T> list, int start, int end, Random rnd = null) where T : IComparable<T>
{
if (rnd != null)
list.Swap(end, rnd.Next(start, end+1));
var pivot = list[end];
var lastLow = start - 1;
for (var i = start; i < end; i++)
{
if (list[i].CompareTo(pivot) <= 0)
list.Swap(i, ++lastLow);
}
list.Swap(end, ++lastLow);
return lastLow;
}
/// <summary>
/// Returns Nth smallest element from the list. Here n starts from 0 so that n=0 returns minimum, n=1 returns 2nd smallest element etc.
/// Note: specified list would be mutated in the process.
/// Reference: Introduction to Algorithms 3rd Edition, Corman et al, pp 216
/// </summary>
public static T NthOrderStatistic<T>(this IList<T> list, int n, Random rnd = null) where T : IComparable<T>
{
return NthOrderStatistic(list, n, 0, list.Count - 1, rnd);
}
private static T NthOrderStatistic<T>(this IList<T> list, int n, int start, int end, Random rnd) where T : IComparable<T>
{
while (true)
{
var pivotIndex = list.Partition(start, end, rnd);
if (pivotIndex == n)
return list[pivotIndex];
if (n < pivotIndex)
end = pivotIndex - 1;
else
start = pivotIndex + 1;
}
}
public static void Swap<T>(this IList<T> list, int i, int j)
{
if (i==j) //This check is not required but Partition function may make many calls so its for perf reason
return;
var temp = list[i];
list[i] = list[j];
list[j] = temp;
}
/// <summary>
/// Note: specified list would be mutated in the process.
/// </summary>
public static T Median<T>(this IList<T> list) where T : IComparable<T>
{
return list.NthOrderStatistic((list.Count - 1)/2);
}
public static double Median<T>(this IEnumerable<T> sequence, Func<T, double> getValue)
{
var list = sequence.Select(getValue).ToList();
var mid = (list.Count - 1) / 2;
return list.NthOrderStatistic(mid);
}
Quelques notes :
O(n)
temps prévu . Si vous voulez O(n)
temps dans le pire des cas alors il existe une technique pour utiliser la médiane de la médiane. Bien que cette technique améliore les performances dans le pire des cas, elle dégrade les performances dans le cas moyen, car la constante en O(n)
est maintenant plus grande. Cependant, si vous calculez la médiane principalement sur de très grandes données, cela vaut la peine de l'examiner.(Count-1)/2
dans un tableau trié. Mais lorsque vous avez un nombre pair d'éléments (Count-1)/2
n'est plus un nombre entier et vous avez deux médianes : La médiane inférieure Math.Floor((Count-1)/2)
y Math.Ceiling((Count-1)/2)
. Certains manuels utilisent la médiane inférieure comme "norme" tandis que d'autres proposent d'utiliser la moyenne des deux. Cette question devient particulièrement critique pour les ensembles de 2 éléments. Le code ci-dessus renvoie la médiane inférieure. Si vous voulez plutôt la moyenne de la médiane inférieure et supérieure, vous devez appeler le code ci-dessus deux fois. Dans ce cas, assurez-vous de mesurer les performances de vos données pour décider si vous devez utiliser le code ci-dessus ou simplement le tri direct.MethodImplOptions.AggressiveInlining
l'attribut Swap<T>
pour des performances légèrement améliorées.
@ShitalShah : re : 6, si je veux calculer la médiane avec la moyenne, au lieu de faire 2 appels à NthOrderStatistic, ne puis-je pas profiter du fait que la liste est mutée et sélectionner essentiellement l'élément suivant. Je ne sais pas si la méthode NthOrderStatistic finit par trier la liste de manière ascendante ou seulement une partie de celle-ci (en fonction des données de la liste en fin de compte).
@costa - NthOrderStatistics n'a pas de garantie sur le tri d'une moitié. L'élément suivant n'est pas non plus garanti d'être l'élément suivant plus petit ou plus grand.
C'est très pratique, merci ! J'ai mis à jour les méthodes pour utiliser les membres du corps de l'expression C# 6 et j'ai collé dans un gist, ainsi qu'un algorithme d'écart type -. gist.github.com/cchamberlain/478bf7a3411beb47abb6
Merci Rafe, cela prend en compte les problèmes que vos répondeurs ont signalés.
public static double GetMedian(double[] sourceNumbers) {
//Framework 2.0 version of this method. there is an easier way in F4
if (sourceNumbers == null || sourceNumbers.Length == 0)
throw new System.Exception("Median of empty array not defined.");
//make sure the list is sorted, but use a new array
double[] sortedPNumbers = (double[])sourceNumbers.Clone();
Array.Sort(sortedPNumbers);
//get the median
int size = sortedPNumbers.Length;
int mid = size / 2;
double median = (size % 2 != 0) ? (double)sortedPNumbers[mid] : ((double)sortedPNumbers[mid] + (double)sortedPNumbers[mid - 1]) / 2;
return median;
}
@richieqianle : Parce que tout ce qui peut être statique devrait l'être. C'est plus efficace du point de vue de tableau des fonctions virtuelles .
@abatishchev Une méthode n'est pas virtuelle par défaut en C# (contrairement à Java). Mais même si elle étaient La performance est une très mauvaise raison de rendre quelque chose statique ou non. Une meilleure raison - au moins dans cette réponse - pourrait être si la méthode est une sorte de méthode utilitaire, où aucune instance de la classe n'est nécessaire.
Math.NET est une bibliothèque opensource qui offre une méthode de calcul de la Médiane . Le paquet nuget s'appelle MathNet.Numerics .
L'utilisation est assez simple :
using MathNet.Numerics.Statistics;
IEnumerable<double> data;
double median = data.Median();
decimal Median(decimal[] xs) {
Array.Sort(xs);
return xs[xs.Length / 2];
}
Ça devrait faire l'affaire.
-- EDIT --
Pour ceux qui veulent tout savoir, voici la solution complète, courte et pure (un tableau d'entrée non vide est supposé) :
decimal Median(decimal[] xs) {
var ys = xs.OrderBy(x => x).ToList();
double mid = (ys.Count - 1) / 2.0;
return (ys[(int)(mid)] + ys[(int)(mid + 0.5)]) / 2;
}
C'est O(n log n)
. Il est possible de trouver la médiane en O(n)
temps. De plus, cette méthode ne retourne pas la médiane dans le cas où le tableau est de longueur égale (elle devrait être la moyenne des deux éléments du milieu après le tri du tableau).
Bien sûr, mais la question ne mentionnait pas O(n) comme une exigence et, en ce qui concerne les cas pairs ou vides, ils ont été laissés comme un exercice pour l'étudiant.
De plus, cela modifie le tableau que vous passez à la méthode, ce qui est tout simplement stupide.
Existe-t-il une fonction dans la bibliothèque mathématique de .net ?
Non.
Il n'est pourtant pas difficile d'écrire le sien. L'algorithme naïf trie le tableau et choisit les éléments du milieu (ou la moyenne des deux éléments du milieu). Cependant, cet algorithme est O(n log n)
alors qu'il est possible de résoudre ce problème en O(n)
temps. Vous voulez regarder algorithmes de sélection pour obtenir un tel algorithme.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.